Recycler源码解读<二> fill 方法分析

深入剖析LinearLayoutManager中的fill方法,理解RecyclerView如何通过此方法填充布局,包括处理滑动引起的View回收,以及布局过程中对空间的计算与分配。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

跟大家说明下,我分析的源码是LinearLayoutManager 里面的fill方法,Recycler系列都是采用这个布局里面的方法。

首先我提出了fill方法的所有源码。

    int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
            RecyclerView.State state, boolean stopOnFocusable) {
        // max offset we should set is mFastScroll + available
        final int start = layoutState.mAvailable;
        if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
            // TODO ugly bug fix. should not happen
            if (layoutState.mAvailable < 0) {
                layoutState.mScrollingOffset += layoutState.mAvailable;
            }
            recycleByLayoutState(recycler, layoutState);
        }
        int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
        LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
        while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
            layoutChunkResult.resetInternal();
            layoutChunk(recycler, state, layoutState, layoutChunkResult);
            if (layoutChunkResult.mFinished) {
                break;
            }
            layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
            /**
             * Consume the available space if:
             * * layoutChunk did not request to be ignored
             * * OR we are laying out scrap children
             * * OR we are not doing pre-layout
             */
            if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
                    || !state.isPreLayout()) {
                layoutState.mAvailable -= layoutChunkResult.mConsumed;
                // we keep a separate remaining space because mAvailable is important for recycling
                remainingSpace -= layoutChunkResult.mConsumed;
            }

            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
                if (layoutState.mAvailable < 0) {
                    layoutState.mScrollingOffset += layoutState.mAvailable;
                }
                recycleByLayoutState(recycler, layoutState);
            }
            if (stopOnFocusable && layoutChunkResult.mFocusable) {
                break;
            }
        }
        if (DEBUG) {
            validateChildOrder();
        }
        return start - layoutState.mAvailable;
    }

*************************************************************************************************************************************************首先看下官方的源码注解吧,非常重要。对把握全局很关键。并且貌似谷歌的源码写的还是非常切中要点的。

    /**
     * The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
     * independent from the rest of the {@link com.android.internal.widget.LinearLayoutManager}
     * and with little change, can be made publicly available as a helper class.
     填充由layoutState 定义的布局。此方法在LinearLayoutManager相当独立,跟其中别的方法关联性不强,可以单独出来形成帮助类。接下来的是传参。
     * @param recycler        Current recycler that is attached to RecyclerView
     * @param layoutState     Configuration on how we should fill out the available space.
     * @param state           Context passed by the RecyclerView to control scroll steps.
     * @param stopOnFocusable If true, filling stops in the first focusable new child

  //布局是否直接布局到第一个有焦点的子布局为止。
     * @return Number of pixels that it added. Useful for scroll functions.
     */

// max offset we should set is mFastScroll + available
        final int start = layoutState.mAvailable;

看下这个      int mAvailable,代表我们需要布局的空间。如果是纵向的那么就是纵向空间。但是如果存在滑动的情况,很显然,这个最大空间还应该加上这个滑动量,mFastScroll。 

  if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
            // TODO ugly bug fix. should not happen
            if (layoutState.mAvailable < 0) {
                layoutState.mScrollingOffset += layoutState.mAvailable;
            }
            recycleByLayoutState(recycler, layoutState);
        }
     如果存在滑动距离,那么调用recycleByLayoutState 方法,现在看这个方法。

  private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
        if (!layoutState.mRecycle || layoutState.mInfinite) {
            return;
        }
        if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
            recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
        } else {
            recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
        }
    }

//看方法是设置recycler的。找到一个recycleViewsFromEnd分析一下。

//官方解释是:回收那些由于滑动移除可视范围内的View。

 private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
        final int childCount = getChildCount();
        if (dt < 0) {
            if (DEBUG) {
                Log.d(TAG, "Called recycle from end with a negative value. This might happen"
                        + " during layout changes but may be sign of a bug");
            }
            return;
        }

        //最末位置的距离减去滑动的距离。
        final int limit = mOrientationHelper.getEnd() - dt;

        //是否是反向布局。目前我们考虑的都是正常的从上往下布局。
        if (mShouldReverseLayout) {
            for (int i = 0; i < childCount; i++) {
                View child = getChildAt(i);

              //判断是否被移除了可视范围,这个时候需要回收此postion的ViewHolder。
                if (mOrientationHelper.getDecoratedStart(child) < limit
                        || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
                    // stop here
                    recycleChildren(recycler, 0, i);
                    return;
                }
            }
        } else {
            for (int i = childCount - 1; i >= 0; i--) {
                View child = getChildAt(i);
                if (mOrientationHelper.getDecoratedStart(child) < limit
                        || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
                    // stop here
                    recycleChildren(recycler, childCount - 1, i);
                    return;
                }
            }
        }
    }

好了。    recycleByLayoutState(recycler, layoutState);分析完毕,主要考虑的是由于滑动造成的回收问题。所以看到RecyclerView的复杂性由于这个缓存机制已经变得复杂了。

 int remainingSpace = layoutState.mAvailable + layoutState.mExtra;

//计算出还剩多少空间可以布局。

现在进入fill方法的核心逻辑。核心逻辑其实就是一个一个View去填充剩余空间。

 while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) 

判断条件是个并,其一是mInfinite 不限制item的数量,或者剩余空间大于0。

hasMore方法是关联adapter,判断adapter是否还有data没有layout。

 

         //重置ChunResult变量。

           ayoutChunkResult.resetInternal();

         //此方法已经在第一篇已经分析了。非常关键的方法,可以再退回去看下。
            layoutChunk(recycler, state, layoutState, layoutChunkResult);

            //是否可结束了。
            if (layoutChunkResult.mFinished) {
                break;
            }

            //计算已经布局的偏移。
            layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
            /**
             * Consume the available space if:
             * * layoutChunk did not request to be ignored
             * * OR we are laying out scrap children
             * * OR we are not doing pre-layout
             */

           //减去可以利用的空间。这个条件中第一个是否忽略掉消费,第二个mScrapList 是否为空。第三个

是否处于layout前状态。
            if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
                    || !state.isPreLayout()) {
                layoutState.mAvailable -= layoutChunkResult.mConsumed;
                // we keep a separate remaining space because mAvailable is important for recycling
                remainingSpace -= layoutChunkResult.mConsumed;
            }

           //计算此mScrollingOffset 的值,这个代表滚动值,当然布局一个view,滚动值也增加了。如果可以利用空间小于0,那么还得减去这个量。这个值代表你累计同一个滚动了多少距离。它主要的逻辑是会一直累计布局的View的消费量。

            if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
                layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
                if (layoutState.mAvailable < 0) {
                    layoutState.mScrollingOffset += layoutState.mAvailable;
                }

   //由于布局一个可能会引起一些进入不可视的状态,走一遍回收过程。
                recycleByLayoutState(recycler, layoutState);
            }

   //判断是否需要停止布局。
            if (stopOnFocusable && layoutChunkResult.mFocusable) {
                break;
            }

至此fill方法方法分析完毕。如果还是有点云里雾里这个很正常,等整个Recycler系列都结束了,再来串其中几个变量的值意义。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值