ListView
ItemView的性质
ViewType
- ListHeader和ListFooter为-2;
- 不进行复用的标记为-1;
- 其余 >= 0。
TransientState
如果1个ItemView正在执行动画,或者记录了用户的某个操作,那么可以调用View.setTransientState(true)把它标记为”是持有瞬时状态的”,那么它将不被复用。
ID
ItemView的Id应该取决于内容的Id。它的作用是作为ItemView的唯一标示,使标记为TransientState的ItemView在入屏时不需要重新创建。
AbsListView.RecyclerBin
AbsListView.RecyclerBin是复用ItemView功能的实现类。其重要成员变量如下:
- mActiveViews(View[]):存放显示在屏幕上的ItemView的引用。
- mCurrentScrap(ArrayList
):当ViewType只有1种时,当ItemView出屏时存放它的引用。 - mScrapViews(ArrayList
[]):当ViewType多于1种时,当ItemView出屏时把它的引用存放在mScrapViews[ViewType]。 - mSkippedScrap(ArrayList
):当不进行复用的ItemView出屏时存放它的引用。 - mTransientStateViewsById(LongSparseArray
):当被标记为TransientState、且有Id的ItemView出屏时存放它的引用。键为Id。 - mTransientStateViews(SparseArray
):当被标记为TransientState、没有Id的ItemView出屏时存放它的引用。键为position。
复用ItemView
复用的ItemView会作为getView()的第二个参数。通过判断它是否为null,来决定是否需要新建ItemView。另外,还可使用ViewHolder来减少findViewById()的调用。
储存ItemView
当ListView显示第一屏或ItemView出屏时发生。
- 把ItemView的position记录到ItemView.mLayoutParams.scrapedFromPosition;
- 按不同情况把ItemView的引用存放到AbsListView.RecyclerBin的不同集合中。
复用ItemView
当ListView显示第一屏或ItemView入屏时发生。
- 先根据Id从mTransientStateViewsById、或根据position从mTransientStateViews取;如果取不到,向下执行;
- 获取该位置的ViewType;如果只有一种ViewType,从mCurrentScrap中取ItemView;否则从mScrapViews[ViewType]中取。
- 取的过程是,遍历mCurrentScrap或mScrapViews[ViewType];
- 如果某个ItemView.mLayoutParams.itemId与该位置的getItemId()相同,那么返回这个ItemView;
- 如果某个ItemView.mLayoutParams.scrapedFromPosition与该位置的position相同,那么返回这个ItemView;
- 如果都没有,那么返回mCurrentScrap或mScrapViews[ViewType]的末项。
- 当是第一屏的时候,集合都是空的,那么会返回null。
RecyclerView
RecyclerView.ViewHolder
RecyclerView的复用单位是ViewHolder,即强制使用ViewHolder优化findViewById()频繁调用的问题;ViewHolder中不仅封装了ItemView及子View的引用,还封装了mItemId、mItemViewType、mPosition、mFlag等。
ID
Adapter的mHasStableIds和getItemId():认实现是mHasStableIds是flase,getItemId()返回NO_ID;这会影响回收时进行detach操作还是remove操作、缓存时使用id还是position作为标示。
ItemView的detach操作
把ViewHolder打上FLAG_TMP_DETACHED标记。从RecyclerView中detach ViewHolder对应的ItemView。如果ViewHolder没有在执行动画,把它加入mAttachScrap,否则加入mChangedScrap。
ItemView的remove操作
如果ViewHolder被标记为FLAG_INVALID、且没有在执行动画、且Adapter.mHasStableIds为false,那么在回收时将进行remove操作。从RecyclerView中remove ViewHolder对应的ItemView,然后判断mCachedScrap缓存是否满了,如果满了,把ViewHolder的引用储存到mRecyclerPoll,否则储存到mCachedScrap。
RecyclerView的四个缓存
- mChangedScrap:缓存被detach的、正在执行动画的ViewHolder;
- mAttachScrap:缓存被detach的、没在执行动画的ViewHolder;
- mCachedScrap:当mCachedScrap中元素数量不足mViewCacheMax时,缓存被detach的ViewHolde;
- mRecyclerPoll:当mCachedScrap满了时,缓存被detach的ViewHolde;
区别
RecyclerView比ListView更有优势,体现在:
- 把布局逻辑抽象成LayoutManager,基于此可以实现多种布局;默认实现的有线性布局、网格布局、瀑布流布局。
- 把ItemView的入场/出场动画逻辑抽象成ItemAnimator,基于此可以实现多种入场/出场动画。
- 使用四级缓存,使滑动更流畅。
- 强制使用ViewHolder,优化频繁调用findViewById()带来的性能问题。
- 可以实现局部刷新。
优化卡顿
- 优化布局嵌套,减少过度绘制区域;
- 在滑动过程中暂停加载图片;
- 避免在滑动监听、getView()、createViewHolder()、bindViewHolder()等方法中进行耗时操作;
- 为ItemView/ViewHolder根据内容绑定Id,充分利用ListView/RecyclerView的缓存复用机制。