就一句话,这个Item是否可见,可见获取得到,不可见获取为空
1.RecyclerView的缓存机制
RecyclerView之所以能滚动,就是因为它在监听到手指滑动之后,不断地更新Item的位置,也就是反复layout
子View了,这部分工作由LayoutManager负责。
LayoutManager在layout
子View之前,会先把RecyclerView的每个子View所对应的ViewHolder都放到mAttachedScrap
中,然后根据当前滑动距离,筛选出哪些Item需要layout
。获取子View对象,会通过getViewForPosition
方法来获取。这个方法就是题目中说的那样:先从mAttachedScrap
中找,再......
Item布局完成之后,会对刚刚没有再次布局的Item进行缓存(回收),这个缓存分两种:
- 取出(即将重用)时无须重新绑定数据(即不用执行
onBindViewHolder
方法)。这种缓存只适用于特定position
的Item(名花有主); - 取出后会回调
onBindViewHolder
方法,好让对应Item的内容能正确显示。这种缓存适用所有同类型的Item(云英未嫁);
第一种缓存,由Recycler.mCachedViews
来保管,第二种放在RecycledViewPool中。
如果回收的Item它的状态(包括:INVALID、REMOVED、UPDATE、POSITION_UNKNOWN)没有变更,就会放到mCachedViews
中,否则扔RecycledViewPool里。
emmmm,除了滚动过程中,会对Item进行回收和重新布局,还有一种就是,当Adapter数据有更新时:
- Inserted:如果刚好插入在屏幕可见范围内,会从RecycledViewPool中找一个相同类型的ViewHolder(找不到就create)来重新绑定数据并layout;
- Removed:会把对应ViewHolder扔到
mAttachedScrap
中并播放动画,动画播放完毕后移到RecycledViewPool里; - Changed:这种情况并不是大家所认为的:直接将这个ViewHolder传到Adapter的
onBindViewHolder
中重新绑定数据。而是先把旧的ViewHolder扔mChangedScrap
中,然后像Inserted那样从RecycledViewPool中找一个相同类型的ViewHolder来重新绑定数据。旧ViewHolder对象用来播放动画,动画播完,同样会移到RecycledViewPool里;
注意:如果是使用notifyDataSetChanged
方法来通知更新的话,那么所有Item都会直接扔RecycledViewPool中,然后逐个重新绑定数据的。
当然了,上面说的这几种情况,不包括瞎写自定义的LayoutManager,因为在自定义的LayoutManger中,怎么去管理缓存,完全出于个人喜好。
好啦,温习完了之后,来看回题目中的问题:
这几个存放缓存的集合,各自的作用以及使用场景?
mAttachedScrap
:LayoutManager每次layout
子View之前,那些已经添加到RecyclerView中的Item以及被删除的Item的临时存放地。使用场景就是RecyclerView滚动时、还有在可见范围内删除Item后用notifyItemRemoved
方法通知更新时;mChangedScrap
:作用:存放可见范围内有更新的Item。使用场景:可见范围内的Item有更新,并且使用notifyItemChanged
方法通知更新时;mCachedViews
:作用:存放滚动过程中没有被重新使用且状态无变化的那些旧Item。场景:滚动,prefetch;RecycledViewPool
:作用:缓存Item的最终站,用于保存那些Removed、Changed、以及mCachedViews满了之后更旧的Item。场景:Item被移除、Item有更新、滚动过程;
写到这里发现漏讲了一个prefetch,好吧,这个prefetch机制就是RecyclerView在滚动和惯性滚动的时候,借助Handler来事先从RecycledViewPool中取出即将要显示的Item,随即扔到mCachedViews
中,这样的话,当layout到这个Item时,就能直接拿来用而不用绑定数据了。
2.如何解决
你需要判断当前可见item有哪些
获取当前RecyclerView可见的第一个和最后的item在整体列表里的顺序
layoutM.findFirstVisibleItemPosition()
layoutM.findLastVisibleItemPosition()
获取某个可见item的View,这里要注意这个输入的参数的起点是以第一个可见item为标准的。
比如你当前第一可见item是6,最后可见item是12,你想要得到8对应的item,那就得传 2
recyc.getChildAt(n-first)
如果是findViewByPosition,则是可以直接通过itemview在整体的位置获取view,注意该itemview是否可见