RecyclerView笔记

参考
基于滑动场景解析RecyclerView的回收复用机制原理
RecyclerView剖析

一、 缓存机制
1.1 scrap缓存

// 添加
 LayoutManager#onLayoutChildren
       // 删除recyclerview中的子view并保存到scrap
       detachAndScrapAttachedViews(recycler);
       // 从4级缓存中查找viewholder后执行addView
// 删除
 RecyclerView#dispatchLayoutStep3
      // 重置scrap,即清空
      mLayout.removeAndRecycleScrapInt(mRecycler);     

提示:mAttachedScrap和mChangedScrap只用在layout过程中的临时变量, 当recyclerview布局完成后(即STATE_IDLE后)scrap是空数组;

1.2 mCachedViews
添加到RecyclerView时不用执行onBindViewHolder函数, 默认大小是2; 从安卓5开始添加了Prefetch功能, 如果使用LinearLayoutManager则大小增1; 如果使用瀑布流StaggeredGridLayoutManager则增加span的个数(例如2列瀑布流则增2)。
mCachedView数量 = mRequestedCacheMax + prefetch数量

在做滑动性能调优时可以增加该数组长度, 减少onBindViewHolder的执行次数;

二、瀑布流
瀑布流最为复杂, 坑也最多。
常见顶部留白和item抖动是由于viewholder在layout时和实际高度有差别;
(百度能搜到一堆,不再赘述)

1、 在onBindViewHolder中设置itemView的宽度和高度为固定值;
2、 设置GAP_HANDLING_NONE
3、 取消默认动画, RecyclerView#setItemAnimator(null);
4、 滑动到顶部时执行invalidateSpanAssignments, 其实就是清空mLazyLookupSpan并requestLayout。

三、布局流程
RecyclerView#setAdapter
RecyclerView.Adapter#notifyDataSetChanged
实际上都是修改标志位,然后执行requestLayout()。 等到下一个Choreographer触发的doTraversal函数;

dispatchLayoutStep1:  preLayout阶段, 会缓存动画信息到mViewInfoStore并可能执行一次mLayout.onLayoutChildren
dispatchLayoutStep2: 真正的布局阶段, 肯定执行mLayout.onLayoutChildren函数;
dispatchLayoutStep3:postLayout阶段,执行mViewInfoStore中的动画并清空scrap数组, 执行mLayout.onLayoutComplete

tip:重载LayoutManager#onLayoutComplete函数表示布局已完成;

四、瀑布流缓存
mLazySpanLookup保存每个item在第几列, 从而滑动列表后记录以前的位置并在重新显示时直接使用。否则使用getNextSpan函数计算出在哪一列;
mSpans数组, 元素个数等于列数。 保存每一列的view和开始、结束座标, 从而在每一列添加view时从mSpans中拿到以前的位置并添加。

mSpans是瀑布流的大坑, 这意味着notifyDataSetChanged后并不会从顶部开始添加view, 而是从mSpans缓存的位置添加。
下拉刷新时添加滑动位置, 其实就是设置参数并在一次layout中完成操作;

notifyDateSetChanged
RecyclerView#scrollToPosition(0);    


                // Child is not visible. Set anchor coordinate depending on in which direction
                // child will be visible.
                anchorInfo.mPosition = mPendingScrollPosition;
                if (mPendingScrollPositionOffset == INVALID_OFFSET) {
                    final int position = calculateScrollDirectionForPosition(
                            anchorInfo.mPosition);
                    anchorInfo.mLayoutFromEnd = position == LayoutState.LAYOUT_END;
                    anchorInfo.assignCoordinateFromPadding();
                } else {
                    anchorInfo.assignCoordinateFromPadding(mPendingScrollPositionOffset);
                }
                anchorInfo.mInvalidateOffsets = true;
    // 执行scrollToPosition(0)后mInvalidateOffsets为true, 并重置了mSpan
    if (getChildCount() > 0 && (mPendingSavedState == null
            || mPendingSavedState.mSpanOffsetsSize < 1)) {
        if (anchorInfo.mInvalidateOffsets) {
            for (int i = 0; i < mSpanCount; i++) {
                // Scroll to position is set, clear.
                mSpans[i].clear();
                if (anchorInfo.mOffset != INVALID_OFFSET) {
                    mSpans[i].setLine(anchorInfo.mOffset);
                }
            }

五、神坑
瀑布流放开item动画后出现诡异问题如被删除的item再次显示到recyclerview(上下滑动后刷新为正确的数据)、间距各种不对。
解决措施:在onScrollListener里的statechanged函数里判断当前正在itemanimation动画则结束动画;

常见问题:
1、滑动列表时先删除再添加view;
2、recyclerview使用观察者模式, 更新各种标志位后调用requestLayout; requestLayout函数可以调用多次, 其实就是设置标志位;
3、瀑布流滑动一段距离后执行notifyDataSetChanged时按照mSpans的位置进行layout。
4、itemdecoration是在RecyclerView的onDraw函数中绘制的。
后续抽时间补上流程图、示意图。

你可能感兴趣的:(RecyclerView笔记)