前言
因为APP设计的原因,Recyclerview是我在Android中最常用的组件,我们公司的APP几乎每一个页面都会包含至少一个Recyclerview,本篇文章主要介绍一些我个人在工作中总结、收集的recyclerview优化经验。
正文
1.不要在onBindViewHolder中设置点击事件和耗时操作
Recyclerview的onBindViewHoler主要负责将数据与holder绑定,它在列表滑动时会不停的被调用。如果在onBindViewHolder中设定监听操作,会导致已经的绑定点击事件的view,被重复绑定监听操作。
点击事件的监听可以在onCreateViewHolder中设定。一些会创建新对象的操作,也需要根据实际情况考虑从onBindViewHolder中迁移到onCreateViewHolder。
注意: onBindViewHolder运行在UI线程中,如果进行了耗时操作,会导致页面卡顿。并且onBindViewHolder中只应该进行数据的绑定,而不应该进行数据的处理和计算等操作。
2.Recyclerview嵌套Recyclerview的优化
Recyclerview嵌套Recyclerview最经典的运用就是,一个纵向滑动的列表内部的每个item是一个可以横向滑动的Recyclerview,比如说GooglePlay。
这种情况可以使用LinearLayoutManager.setInitialPrefetchItemCount()设定
横向列表初次显示时可见item的个数。如果横向滑动的View中数据量很少,并且不需要横向刷新时,也可以考虑使用HorizontalScrollView实现。
关于这个API的官方文档翻译如下:
设置collectInitialPrefetchPositions(int,LayoutPrefetchRegistry)中要预取的项目数,它定义当此LayoutManager的RecyclerView嵌套在另一个RecyclerView中时应预取多少内部项目。
将此值设置为此内部LayoutManager首次滚动到视口时将显示的项目数。 RecyclerView将尝试预取该数量的项目,并提前将这些Item初始化,避免因内部RecyclerView滚动到视图(viewport)中变得混乱。
举个例子,水平滚动的RecyclerViews内部嵌套垂直滚动的RecyclerView。行中始终有4个项目可见(如果没有对齐,则是5个)。为每个内部的RecyclerView的LinearLayoutManager设定setInitialPrefetchItemCount=4,这将使RecyclerView的预取功能能够在屏幕上滚动之前,提前为一行中的4个视图完成创建/绑定工作。
只有在一个Recyclerview嵌套在另一个Recyclerview中,设定collectInitialPrefetchPositions才会生效。
如果将此值设置为大于此视图中可见的视图数可能会导致不必要的绑定工作,并且会增加创建和活动使用的视图数。
3.多个RecycerView共用RecycledViewPool
在大多数APP中都有这样一种场景,一个ViewPager中包含多个Fragment,而Fragment中主体是Recyclerview,并且Recyclerview中item view的布局是相同的。例如 微博等
这种情况下,Recyclerview可以设定统一的缓存池用来提高性能。
新建缓存池:
RecyclerView.RecycledViewPool viewPool=new RecyclerView.RecycledViewPool();
设定缓存池:
recyclerView.setRecycledViewPool(viewPool);
4.精确的更新数据使用DiffUtil
在Recyclerview中提供了多种数据数据刷新方式
- notifyDataChanged()
用于刷新全部数据,会导致整个布局重绘,所有的ViewHolder也会重新绑定。 - notifyItemXXX();
用于刷新指定位置的Item数据。
虽然有了这些刷新方式,但是实际开发中,存在这样一种情况,新数据集与旧数据集仅有一部分数据存在差异。
例如:刷新一个联系人列表,联系人列表中部分联系人的头像有变化,但是姓名和手机号码等信息未发生变化。这种情况以往都是使用notifyDataChanged方法刷新全部数据,但是刷新全部数据的会导致整个布局重绘,Recyclerview中针对这种情况还提供了另一种粒度更小的刷新方式DiffUti
这里不打算去讲DiffUtil的具体用法,只需要记住DiffUtil的使用场景即可:列表中存在多个Item的数据需要刷新,但是新数据集与旧数据集存在重复的情况
5.灵活设定setHasFixedSize
在Recyclerview中使用以下方法时,会触发requestLayout()
- onItemRangeChanged()
- onItemRangeInserted()
- onItemRangeRemoved()
- onItemRangeMoved()
requestLayout()会重新计算item的大小,如果item的布局文件已经将宽高设为固定大小,可以设定setHasFixedSize(true),来避免Recyclerview重新计算item的大小。
6.优化Item的布局
布局优化是个老生长谈的问题,本质上就是减少嵌套,ConstraintLayout是google推出的一个用于减少布局嵌套的新layout,但是在Recyclerview中使用ConstraintLayout会导致cpu使用率上升,暂时不推荐使用,不过ConstraintLayout 2.0版本已经进入beta测试,期待后续会有优化。
7.数据非常少时,使用ListView
不知道你有没有考虑过这样的问题,RecyclerView用已经如此强大,用得人也越来越多,为什么最新的Android系统中ListView依然没有被标注为"过时"。
RecyclerView重新设计了缓存机制,新的缓存机制更加强大,更适合处理大量数据的显示,但是这种缓存机制会占据更多的内存,当一个列表页面实际数据项非常少的时候,ListView往往比RecyclerView更合适。