我正在开发Amazon Fire TV。
因为它是一个电视应用程序(没有触摸),我需要在行的布局内有焦点才能导航。
我有一个非常简单的Recyclerview
图像,文字和可聚焦。当我向上或向下按压时,它都会正确滚动和填充,但我注意到当我导航速度快于滚动时可以跟上,它会创建新的视图(关闭屏幕)并延迟UI。
我创建了一个包含Creation数字的活动。当我慢慢滚动时,最高的创作#是10.但是当我快速滚动时,我会在一秒内获得创作号为60的卡片。这会导致巨大的延迟,并且应用程序会丢失大量帧。我的做法完全错了吗?
使用以下代码对此进行测试。
/**
* Created by sylversphere on 15-04-15.
*/
public class LandfillActivity extends Activity{
private Context context;
private static int ticketNumber;
private static int getTicket(){
ticketNumber ++;
return ticketNumber;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = this;
setContentView(R.layout.landfill_activity);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerView);
GridLayoutManager glm = new GridLayoutManager(context, 2);
recyclerView.setLayoutManager(glm);
SickAdapter sickAdapter = new SickAdapter();
recyclerView.setAdapter(sickAdapter);
}
public class SickViewHolder extends RecyclerView.ViewHolder{
TextView ticketDisplayer;
public ImageView imageView;
public SickViewHolder(View itemView) {
super(itemView);
ticketDisplayer = (TextView) itemView.findViewById(R.id.ticketDisplayer);
imageView = (ImageView) itemView.findViewById(R.id.imageView);
itemView.findViewById(R.id.focus_glass).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
context.startActivity(new Intent(context, LouisVuittonActivity.class));
}
});
}
public void setTicket(int value){
ticketDisplayer.setText(""+value);
}
}
public class SickAdapter extends RecyclerView.Adapter<SickViewHolder>{
@Override
public SickViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
SickViewHolder svh = new SickViewHolder(getLayoutInflater().inflate(R.layout.one_row_element, null));
svh.setTicket(getTicket());
return svh;
}
@Override
public void onBindViewHolder(SickViewHolder holder, int position) {
String[] image_url_array = getResources().getStringArray(R.array.test_image_urls);
Picasso.with(context).load(image_url_array[position % image_url_array.length] ).fit().centerCrop().into(holder.imageView);
}
@Override
public int getItemCount() {
return 100000;
}
}
}
one_row_element.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="200dp"
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:src="@mipmap/sick_view_row_bg" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|center_vertical"
android:layout_marginLeft="15dp"
android:orientation="horizontal">
<TextView
android:id="@+id/virusTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Creation #"
android:textColor="#fff"
android:textSize="40sp" />
<TextView
android:id="@+id/ticketDisplayer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1"
android:textColor="#fff"
android:textSize="40sp" />
</LinearLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/focus_glass"
android:background="@drawable/subtle_focus_glass"
android:focusable="true"
android:focusableInTouchMode="true"/>
</FrameLayout>
</FrameLayout>
test_image_urls.xml(不属于我的网址)
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="test_image_urls"
formatted="false">
<item>http://farm4.static.flickr.com/3175/2737866473_7958dc8760.jpg</item>
<item>http://farm4.static.flickr.com/3276/2875184020_9944005d0d.jpg</item>
<item>http://farm3.static.flickr.com/2531/4094333885_e8462a8338.jpg</item>
<item>http://farm4.static.flickr.com/3289/2809605169_8efe2b8f27.jpg</item>
<item>http://2.bp.blogspot.com/_SrRTF97Kbfo/SUqT9y-qTVI/AAAAAAAABmg/saRXhruwS6M/s400/bARADEI.jpg</item>
<item>http://fortunaweb.com.ar/wp-content/uploads/2009/10/Caroline-Atkinson-FMI.jpg</item>
<item>http://farm4.static.flickr.com/3488/4051378654_238ca94313.jpg</item>
<item>http://farm4.static.flickr.com/3368/3198142470_6eb0be5f32.jpg</item>
<item>http://www.powercai.net/Photo/UploadPhotos/200503/20050307172201492.jpg</item>
<item>http://www.web07.cn/uploads/Photo/c101122/12Z3Y54RZ-22027.jpg</item>
<item>http://www.mitravel.com.tw/html/asia/2011/Palau-4/index_clip_image002_0000.jpg</item>
<item>http://news.xinhuanet.com/mil/2007-05/19/xinsrc_36205041914150623191153.jpg</item>
<item>http://ib.berkeley.edu/labs/koehl/images/hannah.jpg</item>
<item>http://down.tutu001.com/d/file/20110307/ef7937c2b70bfc2da539eea9df_560.jpg</item>
<item>http://farm3.static.flickr.com/2278/2300491905_5272f77e56.jpg</item>
<item>http://www.pic35.com/uploads/allimg/100526/1-100526224U1.jpg</item>
<item>http://img.99118.com/Big2/1024768/20101211/1700013.jpg</item>
<item>http://farm1.static.flickr.com/45/139488995_bd06578562.jpg</item>
</string-array>
</resources>
subtle_focus
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="@color/glass_focus"/>
<item android:drawable="@color/glass_normal"/>
</selector>
glass_normal
是#9000
glass_focus
是#0000
答案 0 :(得分:16)
尝试增加池中的maximum number of recycled views:
recyclerView.getRecycledViewPool().setMaxRecycledViews(50);
50是任意数字,您可以尝试更高或更低,看看会发生什么。
RecyclerView会尝试避免重复使用transient state的视图,因此如果视图快速无效或正在制作动画,则可能无法立即重复使用。
同样,如果你有许多较小的视图,你可能会在屏幕上显示比默认池大小可以处理的更多(更像网格布局)。
答案 1 :(得分:3)
Picasso正在阻止你,但建议自己的机制的建议解决方案不是可行的方法。
毕加索在过去一年左右落后了,今天有更好的替代方案,Google Glide和Facebook Fresco,它们专门发布了更新,以便更好地使用RecyclerView,并证明更快,更多在许多可在线获得的测试中有效地加载,缓存和存储,例如:我希望有所帮助。 祝你好运。
答案 2 :(得分:2)
评论者指出毕加索的待决回应可能会阻碍你。如果是这种情况,您可以通过扩展ImageView
并覆盖以下方法来解决此问题。我觉得值得一试。
@Override
protected void onDetachedFromWindow() {
Picasso.with(context).cancelRequest(this);
super.onDetachedFromWindow();
}
原来这不是正确的方法,任何想要取消请求的人都应该在onViewRecycled()
回调中这样做,如下面的评论中所指出的那样。
答案 3 :(得分:0)
通过深入挖掘Sam Judd的答案,我强制回收者通过在其适配器中实现以下内容来回收视图。
@Override
public boolean onFailedToRecycleView(@NonNull VH holder)
return true;
}
正如您所见here:
如果由此适配器创建的ViewHolder由于其瞬态状态而无法回收,则由RecyclerView调用。收到此回调后,适配器可以清除影响View瞬态的动画并返回true,以便可以回收View。请记住,有问题的视图已从RecyclerView中删除。
在某些情况下,虽然它具有瞬态,但可以回收View。大多数情况下,当View反弹到新位置时,会在onBindViewHolder(ViewHolder,int)调用中清除瞬态。出于这个原因,RecyclerView将决定权交给适配器,并使用此方法的返回值来决定是否应该回收View。
请注意,当所有动画由RecyclerView.ItemAnimator创建时,您永远不应该收到此回调,因为RecyclerView会将这些视图保留为子视图,直到其动画完成为止。当项目视图的子项创建使用RecyclerView.ItemAnimator可能不容易实现的动画时,此回调非常有用。
你永远不应该通过调用holder.itemView.setHasTransientState(false)来解决这个问题。除非您之前调用了holder.itemView.setHasTransientState(true);.每个View.setHasTransientState(true)调用必须与View.setHasTransientState(false)调用匹配,否则,View的状态可能会变得不一致。您应该总是喜欢结束或取消触发瞬态的动画,而不是手动处理动画。
答案 4 :(得分:-2)
对于其他寻找快速黑客的人, 做这个。这将延迟选择,直到它被充气和选择。 我不知道它是如何工作的。 只需在onFocusSearchFailed上返回null。
/**
* Created by sylversphere on 15-04-22.
*/
public class SomeGridLayoutManager extends GridLayoutManager{
private final Context context;
public SomeGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
this.context = context;
}
public SomeGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
this.context = context;
}
@Override
public View onFocusSearchFailed(View focused, int focusDirection, RecyclerView.Recycler recycler, RecyclerView.State state) {
return null;
}
}