Recycler源码解读<一> layoutChunk 函数分析

详细解析了RecyclerView中的layoutChunk方法,这是RecyclerView布局流程中的关键部分。文章深入分析了如何从Recycler中取出View对象,进行布局、测量及定位的过程,并讨论了布局方向、View测量和空间消耗的计算。

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

此为recyclerView 布局中最终落脚方法了。先看整个源码。

    void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
            LayoutState layoutState, LayoutChunkResult result) {
        View view = layoutState.next(recycler);
        if (view == null) {
            if (DEBUG && layoutState.mScrapList == null) {
                throw new RuntimeException("received null view when unexpected");
            }
            // if we are laying out views in scrap, this may return null which means there is
            // no more items to layout.
            result.mFinished = true;
            return;
        }
        LayoutParams params = (LayoutParams) view.getLayoutParams();
        if (layoutState.mScrapList == null) {
            if (mShouldReverseLayout == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
                addView(view);
            } else {
                addView(view, 0);
            }
        } else {
            if (mShouldReverseLayout == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
                addDisappearingView(view);
            } else {
                addDisappearingView(view, 0);
            }
        }
        measureChildWithMargins(view, 0, 0);
        result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
        int left, top, right, bottom;
        if (mOrientation == VERTICAL) {
            if (isLayoutRTL()) {
                right = getWidth() - getPaddingRight();
                left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
            } else {
                left = getPaddingLeft();
                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
            }
            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                bottom = layoutState.mOffset;
                top = layoutState.mOffset - result.mConsumed;
            } else {
                top = layoutState.mOffset;
                bottom = layoutState.mOffset + result.mConsumed;
            }
        } else {
            top = getPaddingTop();
            bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);

            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                right = layoutState.mOffset;
                left = layoutState.mOffset - result.mConsumed;
            } else {
                left = layoutState.mOffset;
                right = layoutState.mOffset + result.mConsumed;
            }
        }
        // We calculate everything with View's bounding box (which includes decor and margins)
        // To calculate correct layout position, we subtract margins.
        layoutDecoratedWithMargins(view, left, top, right, bottom);
        if (DEBUG) {
            Log.d(TAG, "laid out child at position " + getPosition(view) + ", with l:"
                    + (left + params.leftMargin) + ", t:" + (top + params.topMargin) + ", r:"
                    + (right - params.rightMargin) + ", b:" + (bottom - params.bottomMargin));
        }
        // Consume the available space if the view is not removed OR changed
        if (params.isItemRemoved() || params.isItemChanged()) {
            result.mIgnoreConsumed = true;
        }
        result.mFocusable = view.isFocusable();
    }

*************************************************************************************************************************************************

开始源码分析:

        View view = layoutState.next(recycler); 从recycler 取出View对象。


        /**
         * Gets the view for the next element that we should layout.

         获取接下来需要布局的View。同时更新目前的item的索引。
         * Also updates current item index to the next item, based on {@link #mItemDirection}
         *
         * @return The next element that we should layout.
         */

        如果获取的view为空。那么直接返回了。意味着recyview已经布局完毕。这个地方是结束标志,重要。接下来就是正常的布局View的逻辑。
        if (view == null) {
            if (DEBUG && layoutState.mScrapList == null) {
                throw new RuntimeException("received null view when unexpected");
            }
            // if we are laying out views in scrap, this may return null which means there is
            // no more items to layout.
            result.mFinished = true;
            return;
        }

  实际情况,布局是测量的后一步。布局之前必须测量。子View还没添加进入ViewGroup,也没有测量。下面的步骤就是。  LayoutParams params = (LayoutParams) view.getLayoutParams();

获取View的布局参数。如果废弃的mScrapList  为空的逻辑。第一次布局为空。
        if (layoutState.mScrapList == null) { 

// 判断布局的方向,分为从顶部和从底部两个方向。
            if (mShouldReverseLayout == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
                addView(view);

//将View加入child 里面。暂时不分析里面的细节。
            } else {
                addView(view, 0);
            }
        } else {
            if (mShouldReverseLayout == (layoutState.mLayoutDirection
                    == LayoutState.LAYOUT_START)) {
                addDisappearingView(view);
            } else {
                addDisappearingView(view, 0);
            }
        }

//对View进行测量。实际上大家发现,这个过程把加入,测量,布局都融合一块了。细节暂时不分析。    

 measureChildWithMargins(view, 0, 0);

//测量完毕,分析此View 消耗了多少的空间。此处如果是纵向的布局,会分析此View占据的高度。加上padingTop和paddingBottom

result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);

 

  int left, top, right, bottom;
        if (mOrientation == VERTICAL) {
            if (isLayoutRTL()) {

              //垂直布局,但是方向是从右往做开始布局。
                right = getWidth() - getPaddingRight();
                left = right - mOrientationHelper.getDecoratedMeasurementInOther(view);
            } else {
                left = getPaddingLeft();
                right = left + mOrientationHelper.getDecoratedMeasurementInOther(view);
            }

           /./计算view布局的bottom和top值,那么需要检查布局的顺序是从上到下,还是从下到上。
            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                bottom = layoutState.mOffset;
                top = layoutState.mOffset - result.mConsumed;
            } else {

               //从上往下的布局。
                top = layoutState.mOffset;
                bottom = layoutState.mOffset + result.mConsumed;
            }
        } else { 

         //线性布局约束的水平方向。同理。不在分析。
            top = getPaddingTop();
            bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view);

            if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
                right = layoutState.mOffset;
                left = layoutState.mOffset - result.mConsumed;
            } else {
                left = layoutState.mOffset;
                right = layoutState.mOffset + result.mConsumed;
            }
        }

     //开始对View进行layout。这一步之后View就布局彻底结束。

   layoutDecoratedWithMargins(view, left, top, right, bottom);

 // Consume the available space if the view is not removed OR changed

 //设置mIgnoreConsumed的值。此值最后啥用得返回到上层调用。后续章节分析。
        if (params.isItemRemoved() || params.isItemChanged()) {
            result.mIgnoreConsumed = true;
        }

// 设置是否聚焦。因为recycler的布局可以布局到聚焦点结束这么个变量。所以此处传出去。
        result.mFocusable = view.isFocusable();

此方法整体分析完毕了。后续会把recyview的布局和事件传递整体分析出来。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值