LinearLayoutManager浅析

刚开始我想从onLayoutChildren开始看起,看得一脸懵逼。于是想从滑动着手。

在滑动时会调用int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state)这个方法,我分了几个部分。
(建议结合源码来看)

第一部分

1 如果没有child或者dy ==0, 直接返回0
2 mLayoutState.mRecycle = true;
3 layoutDirection dy > 0的话是LayoutState.LAYOUT_END(底部item要出来)再取dy绝对值

第二部分

这一部分的主角是updateLayoutState方法

updateLayoutState(int layoutDirection (END), int requiredSpace(absdy),
                    boolean canUseExistingSpace(true), RecyclerView.State state) 

1 mLayoutState.mLayoutDirection = layoutDirection(END); mLayoutState.mItemDirection = ITEM_DIRECTION_TAIL
2 获取最下边一个child,mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection;
getPosition是获取view的layoutPosition,再加1,赋给mCurrentPosition
3 看
public int getDecoratedEnd(View view) {
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return child.getBottom() + getBottomDecorationHeight(child) + params.bottomMargin;
}
getDecoratedEnd得到的是,这个view的下边界(y坐标),赋给mLayoutState.mOffset
4 getEndAfterPadding()得到recyclerView的内容下边界(y坐标),scrollingOffset就是再滑动多少距离就会到达最下边view的下边界
5 mLayoutState.mAvailable = requiredSpace(absdy)- scrollingOffset;
这里分析一下:
如果mAvailable<0, 即滑动后最下边view的下边界没进入recyclerView, 就是最下边view还有一部分在recyclerView外边
如果mAvailable>0, 即滑动后最下边的view已经全部进入recyclerView了
6 mLayoutState.mScrollingOffset = scrollingOffset
总结:updateLayoutState记录滑动方向,最上边或最下边view的position、view的边界值,此次滑动需不需要添加view

第三部分

这一部分的主角是fill方法

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
             RecyclerView.State state, boolean stopOnFocusable (false) )

1 如果mAvailable<0, 即滑动后最下边view的下边界没进入recyclerView,mScrollingOffset变成absdy
2 现在进入recycleByLayoutState方法,里面我们看到recycleViewsFromEnd和recycleViewsFromStart方法,这2个方法原理是一样的,这里只讲一个

private void recycleViewsFromStart(RecyclerView.Recycler recycler, int dt) {
   ...........(省略)
   for (int i = 0; i < childCount; i++) {
        View child = getChildAt(i);
        if (mOrientationHelper.getDecoratedEnd(child) > limit(就是mScrollingOffset)
            || mOrientationHelper.getTransformedEndWithDecoration(child) > limit) {
          // stop here
          recycleChildren(recycler, 0, i);
          return;
        }
      }
}

这段循环找出,移动了limit之后,位于最上面的view; 那么这个view的上面那些view可以回收掉(它们已经超出了recyclerView)

这个Limit就是layoutState的mScrollingOffset,前面我们说到,这个mScrollingOffset会变,那来分析一下
第一种情况,滑动后最下边view的下边界没进入recyclerView,这时mScrollingOffset就是absdy滑动距离,这个容易理解
第二种情况,滑动后最下边view的下边界已经进入recyclerView里面了,这时mScrollingOffset就是滑动多少最下边的view刚好全部进入recyclerView,就是说最下边view刚好全部进入recyclerView时就要开始回收 (讲得有点乱~,见谅)

现在开始recycleChildren,传入的参数0和i 表示回收从0到i-1的view,调用的是layoutManager的removeAndRecycleViewAt(index, recycler)

具体是让recyclerView remove掉view,然后调用recycler.recycleView(),viewHolder会进入Recycler的mCachedViews(arrayList),如果mCachedViews已满,那就放到ReycleViewPool

现在回到fill方法,我们已经完成了回收view,接下来就要添加view。

首先看remainingSpace,还记得前面说过的,mAvailable>0就是最下边的view滑动后会完全进入recyclerView,那remainingSpace会大于0
再看下面的while,循环的条件是remainingSpace大于0并且还有数据,为什么要用while?应该是刚开始的时候要填满recyclerView。 添加view就在layoutChunk方法里面,进去看看:

添加view调用了layoutManager的addView方法
然后,measureChildWithMargins(view, 0, 0)就会调用view.measure,里面比较简单,会考虑padding,margin,decoration。值得注意的是,例如recyclerView垂直方向滚动,view的垂直方向是wrap_content,那view最终得到的specMode是UNSPECIFIED

然后调用getDecoratedMeasurement(view),垂直情况下,得到view的高度+上下margin+上下decoration,注意因为调用了view.getMeasuredHeight,所以要先测量。

接下来,我们看VERTICAL,RTL是rightToLeft,一般用不上,所以直接看else。其实这一段就是计算添加的这个view的left, right, top, bottom四个参数,就是放在哪里,比较简单。

layoutDecoratedWithMargins这个方法真正放置view,后面4个参数就是view加上margin加上decoration后的结果,传入之后,又会计算出view的实际大小。

总结:fill方法做了回收view和添加view两件事。通过判断滑动后view会不会完全滑出来决定回收,添加view的条件是出现空白空间并且还有数据,先add到recyclerView,再测量view,再决定view的最终位置

后面是一些数学计算,我分析出来了再来补充

下面的mOrientationHelper.offsetChildren(-scrolled) 就是移动子view

scrollBy方法的返回值,根据打LOG,正常滑动的时候就是dy

ThreadLocal 是 Java 中的一个类,它提供了一种线程局部变量的机制。线程局部变量是指每个线程都有自己的变量副本,每个线程对该变量的访问都是独立的,互不影响。 ThreadLocal 主要用于解决多线程并发访问共享变量时的线程安全问题。在多线程环境下,如果多个线程共同访问同一个变量,可能会出现竞争条件,导致数据不一致或者出现线程安全问题。通过使用 ThreadLocal,可以为每个线程提供独立的副本,从而避免了线程安全问题。 ThreadLocal 的工作原理是,每个 Thread 对象内部都维护了一个 ThreadLocalMap 对象,ThreadLocalMap 是一个 key-value 结构,其中 key 是 ThreadLocal 对象,value 是该线程对应的变量副本。当访问 ThreadLocal 的 get() 方法时,会根据当前线程获取到对应的 ThreadLocalMap 对象,并从中查找到与 ThreadLocal 对象对应的值。如果当前线程尚未设置该 ThreadLocal 对象的值,则会通过 initialValue() 方法初始化一个值,并将其存入 ThreadLocalMap 中。当访问 ThreadLocal 的 set() 方法时,会将指定的值存入当前线程对应的 ThreadLocalMap 中。 需要注意的是,ThreadLocal 并不能解决共享资源的并发访问问题,它只是提供了一种线程内部的隔离机制。在使用 ThreadLocal 时,需要注意合理地使用,避免出现内存泄漏或者数据不一致的情况。另外,由于 ThreadLocal 使用了线程的 ThreadLocalMap,因此在使用完 ThreadLocal 后,需要手动调用 remove() 方法清理对应的变量副本,以防止内存泄漏。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值