RecylerView的回收机制分为两部分:Cached View
和RecycledViewPool
当要显示新的view时,取用顺序。
要显示的View --> View Cache --> RecycledViewPool
View Cache:
default size: 2
View Cache中的ViewHolder还保留了position信息,当recyclerview还要显示position的view时,可以直接使用其中的ViewHolder,不用重新bind。
RecycledViewPool:
每种view type 的default size: 5
RecycledViewPool中的ViewHolder已经被清理了position信息,还有一些其他信息,要重新显示是需要重新bind。
放入RecycledViewPool的条件:
- A view went out of RecyclerView’s bounds during scrolling. (cached view 放不下了)
- Data has changed so that the view is no longer seen. Addition to the pool happens when the disappearance animation finishes.
- An item in the view cache has been updated or removed.
- During a search for a ViewHolder, one was found in scrap or cache with position we want, but turned out to be unsuitable because of the wrong view type or id (if adapter has stable ids).
- The LayoutManager added a view at pre-layout, but didn’t add that view in post-layout.
有两种条件会阻碍ViewHolder放入RecycledViewPool中:
Recyclability 为false (during animation)
Transient state 为 True (during animator)
如果上述两个条件都失败了,则回调用Adapter的OnFailedToRecycleView
,返回True,代表强行回收,返回False,代表放入Pool。
如果一个ViewHolder成功回收了,会调用onViewRecycled
View Cache和RecylerView的填充顺序:
先填充View Cache,再填充Pool
一些例子
例子1:
6,7正在显示,4,5在View Cache中,3在Pool中,当向下滑动时,6进入Cache,8从Pool中reuse 3,4从Cache中进入Pool。
例子2:
向上滑动时,5还在Cache中,可以直接显示,7进入Cache。
上面的例子中Pool中的ViewHolder始终保持在1个
例子3:
当调用Adapter.notifyDataSetChanged()
(或者 Adapter.notifyItemRangeChanged()
),会当前所有显示的ViewHolder都进入到Pool,但是如果Pool的数量小于显示的ViewHolder的数量,那么有一部分ViewHolder会不能进入Holder,这时重新显示时,不够的ViewHolder会create,然后再bind。scrollToPosition也会导致需要回收很多Holder到Pool。
一种解决方法是在操作之前增大Pool的capacity,然后操作完之后再设置回默认的。
recyclerView.getRecycledViewPool().setMaxRecycledViews(0, 20);
adapter.notifyDataSetChanged();
new Handler().post(new Runnable() {
@Override
public void run() {
recyclerView.getRecycledViewPool()
.setMaxRecycledViews(0, 1);
}
});