ScrollView嵌套ListView导致item显示不全的原因

一句话总结:

ScrollView重写了它的父类FrameLayout的measureChild和measureChildWithMargins方法,使传入子类的HeightMeasureSpec的模式为UNSPECIFIED,导致listview计算高度时跳过了measureHeightOfChildren方法的执行,只计算了第一个item的高度。

分析:

首先看listview的onMeasure方法中关于高度的赋值:
      1276行:
      int heightSize = MeasureSpec.getSize(heightMeasureSpec);//从heightMeasureSpec中获取高度

      1308行:
      //如果为UNSPECIFIED则高度为第一个child的高度加上边距
      if (heightMode == MeasureSpec.UNSPECIFIED) {
           heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                              getVerticalFadingEdgeLength() * 2;
          }
      1313行:
      //如果为AT_MOST则去执行measureHeightOfChildren方法,计算最大高度
      if (heightMode == MeasureSpec.AT_MOST) {
      // TODO: after first layout we should maybe start at the first visible position, not 0
         heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1);
      }

可以看出listview的高度计算取决于传入onMeasure方法的MeasureSpec的mode类型。通过了解View的测量流程可以知道,onMeasure方法在measure方法中被调用。而measure方法一般被其父view调用。此嵌套情况下,listview的父view是scrollview,那么现在去看一下scrollview中onMeasure的情况:

     super.onMeasure(widthMeasureSpec, heightMeasureSpec);

     if (!mFillViewport) {
                return;
         }
     ......

注意开始的这两行判断,mFillViewport默认是false的,在我们没有人为设置的情况下,onMeasure执行到这里就return了,下面的代码是不会执行的。所以真正执行的应该只有super.onMeasure(widthMeasureSpec, heightMeasureSpec);调用父类的onMeasure方法,ScrollView的父类是Framlayout,Framlayout的高度测量调用的是ViewGroup提供的getChildMeasureSpec方法。此方法内只有当父view的MeasureSpec的mode为UNSPECIFIED时,子view才有可能被赋予UNSPECIFIED。(通过实际测试,FrameLayout嵌套ListView并不会对ListView产生影响。通过DEBUG也发现ScrollView调用父类onMeasure方法时传入的heightMeasureSpec的mdoe也并非是UNSPECIFIED)。那么可以确定,还是ScrollView自身对ListView产生了影响,与FrameLayout关系不大。那么视线就转到了ScrollView对FrameLayout的方法的重写上:

@Override
protected void measureChild(View child, int parentWidthMeasureSpec,
       int parentHeightMeasureSpec) {

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

            child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
           }

@Override
protected void measureChildWithMargins(View child, int parentWidthMeasureSpec, int widthUsed,int parentHeightMeasureSpec, int heightUsed) {

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

       child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
          }

可以看到ScrollView重写了measureChild和measureChildWithMargins方法,并将childHeightMeasureSpec的mode强制更改为UNSPECIFIED。调用的父类的onMeasure方法中执行的measureChildWithMargins方法其实是ScrollView重写过的。最终导致ListView的heightMode为UNSPECIFIED。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值