ScrollView嵌套RecyclerView解决以及原理详解

本文探讨了在ScrollView中嵌套RecyclerView时遇到的问题,详细解释了ScrollView的onMeasure过程,指出RecyclerView未能正确测量高度的原因并非如常认为的那样。通过查看源码,发现RelativeLayout能正确处理而LinearLayout不行。文章旨在帮助开发者理解这一问题并找到解决方案。
摘要由CSDN通过智能技术生成

我们项目中经常可能会用到类似的以下布局。
这里写图片描述
在这种情况下,RecyclerView会经常无法测量出来实际的高度,我一开始以为RecyclerView类似于ListView对MeasureSpec.UNSPECIFIED做了直接高度处理而无法正确测量,但我查看源码后发现不是这样的。网上查找的解决方法是嵌套RelativeLayout。那么为什么RelativeLayout能正确测量而LinearLayout却不行呢,直接查看源码来简单了解一下。

一、ScrollView的onMeasure

首先我们查看ScrollView的onMeasure方法。onMeasure调用了super,然后走到measureChildWithMargins()

 @Override
    protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int usedTotal = mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin +
                heightUsed;
        final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
                Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
                MeasureSpec.UNSPECIFIED);

        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

这里我们只关注这里

final int childHeightMeasureSpec = MeasureSpec.makeSafeMeasureSpec(
                Math.max(0, MeasureSpec.getSize(parentHeightMeasureSpec) - usedTotal),
                MeasureSpec.UNSPECIFIED);

childHeightMeasureSpec 也就是mode是UNSPECIFIED,size是ScrollView的高度,这里usedTotal=0。

二、LinearLayout的OnMeasure

说实话LinearLayout的OnMeasure真的相当的长,但是我们也只需要找关键的地方,weight的处理可以暂时不用管。
在measureVertical()方法中我们直接找到测量规格是如何传入子View的。

   final int usedHeight = totalWeight == 0 ? mTotalLength : 0;
   measureChildBeforeLayout(child, i, widthMeasureSpec, 0,heightMeasureSpec, usedHeight);

其实我可以按照实现的情况来猜测,其实就是将LinearLayout的测量规格加上child已经使用的控件来综合判断下一个child是什么规格的。我接着看源码。
直接调用了ViewGroup的measureChildWithMargins

protected void measureChildWithMargins(View child,
            int parentWidthMeasureSpec, int widthUsed,
            int parentHeightMeasureSpec, int heightUsed) {
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }

那么根据实际情况就可以再猜测这个方法get

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值