我基于创建列表和卡片指南创建了RecyclerView示例。我的适配器仅实现布局填充的模式。
问题在于滚动性能差。即使只有8个项目,RecycleView也存在这个问题。
在一些测试中,我发现在Android L上不会出现此问题。但在KitKat版本中,性能下降是明显的。
我基于创建列表和卡片指南创建了RecyclerView示例。我的适配器仅实现布局填充的模式。
问题在于滚动性能差。即使只有8个项目,RecycleView也存在这个问题。
在一些测试中,我发现在Android L上不会出现此问题。但在KitKat版本中,性能下降是明显的。
最近我也遇到了同样的问题,这是我在最新的RecyclerView支持库中所做的:
用新的优化约束布局(ConstraintLayout)替换复杂布局(嵌套视图、RelativeLayout)。 在Android Studio中启用它: 进入SDK管理器-> SDK工具选项卡-> 支持库-> 选中 ConstraintLayout for Android 和 Solver for ConstraintLayout。添加到依赖项:
compile 'com.android.support.constraint:constraint-layout:1.0.2'
如果可能的话,使RecyclerView的所有元素具有相同的高度。并添加:
recyclerView.setHasFixedSize(true);
使用默认的RecyclerView绘制缓存方法,并根据您的情况进行微调。您不需要第三方库来实现:
recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
如果您使用了许多图像,请确保它们的大小和压缩率是最优的。缩放图像也可能会影响性能。问题有两个方面 - 使用的原始图像和解码后的位图。以下示例为您提供了如何解码从web下载的图像的提示:
InputStream is = (InputStream) url.getContent();
BitmapFactory.Options options = new BitmapFactory.Options();
options.inPreferredConfig = Bitmap.Config.RGB_565;
Bitmap image = BitmapFactory.decodeStream(is, null, options);
最重要的部分是指定inPreferredConfig
- 它定义了每个图像像素将使用多少字节。请注意,这是一个首选选项。如果源图像具有更多颜色,则仍将使用不同的配置进行解码。
确保 onBindViewHolder() 尽可能地廉价。您可以在 onCreateViewHolder()
中设置 OnClickListener 一次,并通过 Adapter 外部传递已点击的项目来调用接口上的监听器。这样你不会一直创建额外的对象。在此处更改视图之前,还应检查标志和状态。
viewHolder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Item item = getItem(getAdapterPosition());
outsideClickListener.onItemClicked(item);
}
});
当数据发生变化时,尝试仅更新受影响的项目。例如,在添加/加载更多项目时,不要使用notifyDataSetChanged()
使整个数据集失效,而是只需使用以下方法: adapter.notifyItemRangeInserted(rangeStart, rangeEnd);
adapter.notifyItemRemoved(position);
adapter.notifyItemChanged(position);
adapter.notifyItemInserted(position);
来自Android开发者网站:
仅在没有其他选择时才使用notifyDataSetChanged()。
但如果需要使用它,请使用唯一的id来维护您的项目:
adapter.setHasStableIds(true);
当使用此方法时,RecyclerView将尝试为报告具有稳定ID的适配器合成可见的结构更改事件。这可以帮助进行动画和视觉对象持久化,但是单个项目视图仍需要重新绑定和重新布局。
即使您做得一切都正确,RecyclerView仍然可能没有像您想要的那样流畅地运行。
recyclerView.setItemViewCacheSize(20);
可以提高性能。但是 recyclerView.setDrawingCacheEnabled(true);
和 recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
呢!我不确定这些会改变什么。这些是 View
特定的调用,允许您以编程方式检索绘图缓存作为位图,并在以后为您的优势使用它。RecyclerView
似乎对此没有任何影响。 - Abdelhakim AKODADI我发现了至少一种可以拖慢你的性能的模式。请记住,onBindViewHolder()
会频繁地被调用。因此,在该代码中进行任何操作都有可能使性能降到谷底。如果您的RecyclerView进行任何自定义操作,很容易在此方法中无意中添加一些缓慢的代码。
我正在根据位置更改每个RecyclerView的背景图像。但是加载图像需要一些工作,这导致我的RecyclerView变得缓慢和卡顿。
为图像创建缓存效果非常好;现在,onBindViewHolder()
只需要修改对已缓存图像的引用,而不是从头加载图像。现在,RecyclerView快速运行。
我知道并非每个人都会遇到这个确切的问题,因此我不打算提供代码。但请将在onBindViewHolder()
中执行的任何工作视为可能导致差劲RecyclerView性能的瓶颈。
我就RecyclerView的性能进行了一次讲解。这里有英文幻灯片和俄语录制视频。
其中包含一系列技术(其中一些已经由@Darya的回答涵盖)。
以下是简要总结:
如果Adapter
的项大小是固定的,则设置:
recyclerView.setHasFixedSize(true);
如果数据实体可以由长整数表示(例如hashCode()
),则设置:
adapter.hasStableIds(true);
并实现:
// YourAdapter.java
@Override
public long getItemId(int position) {
return items.get(position).hashcode(); //id()
}
在这种情况下,Item.id()
将无法工作,因为即使Item
的内容已更改,它仍将保持不变。
P.S. 如果您正在使用DiffUtil,则不需要执行此操作!
使用正确缩放的位图。不要重复发明轮子,使用库。
了解如何选择here。
始终使用最新版本的RecyclerView
。例如,在25.1.0
中有巨大的性能提升-预取。
更多信息here。
使用DiffUtill。
DiffUtil is a must.
Official documentation。
简化您的项目布局!
丰富TextView的微小库 - TextViewRichDrawable
请查看slides以获取更详细的解释。
getItemId
。 - Bitwise DEVSsetHasStableId
标志是否能解决您的问题。根据您提供的信息,您的性能问题可能与内存问题有关。您的应用程序在用户界面和内存方面的性能是相当相关的。在我的RecyclerView中,我使用位图图像作为item_layout的背景。
@Galya说的一切都是正确的(我感谢他的出色答案)。但是它们对我没有起作用。
这就是解决我的问题的方法:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;
Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);
如果想了解更多信息,请阅读这个答案。
recyclerView.setNestedScrollingEnabled(false);
另外,检查您放置RecyclerView的父布局也非常重要。当我在NestedScrollView中测试RecyclerView时,遇到了类似的滚动问题。在另一个可滚动视图中滚动的视图可能会在滚动期间性能下降。
在我的情况下,我发现导致滞后的显著原因是在#onBindViewHolder()
方法内频繁加载可绘制对象。我通过在ViewHolder内只加载一次图像并从该方法中访问它来解决了这个问题。这就是我所做的全部。