ScrollView嵌套GridView解决办法的原因

注:MATCH_PARENT=-1,WRAP_CONTENT=-2
话说这个问题网上的答案千百篇,就我所查阅的来说,基本就一个解决办法:自定义一个GridView,重写其onMeasure方法

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    }

确实很有效。
如果不重写这个方法的话,一般只能显示GridView第一行的高度。
但是,Why?
1.为什么不重写就只能显示第一行的高度
2.为什么重写了就能正常显示了
这说起来真是篇幅很长啊,我还是先举一个简单的例子来看一下ScrollView在测量孩子时做出的一些特殊处理。
如果我有这样一个布局

<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#660000ff"
     >
    <TextView
        android:background="#66ff0000"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="@string/hello_world" />
</ScrollView>

但是显示的结果却是
这里写图片描述
这又是何解呢?
那么我还是通过断点调试的方法看一看整个测试的过程。
我选择在ScrollView的onMeasure()出打上断点,然后他走父类的onMeasure()方法
然后走到这一行 if (mMeasureAllChildren || child.getVisibility() != GONE) {
后面一个条件是成立的,进入if语句,下面这个方法很重要,很重要

measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);

上面的两个参数是ScrollView的,分别是16进制的140和197,即十进制的320和407.
一旦进入了这个函数,孩子的一切都尘埃落定了,不管你的高度是-1,-2,还是一个特定的值,ScrollView一概忽视,它只调用这个方法final int childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(lp.topMargin+lp.bottomMargin, MeasureSpec.UNSPECIFIED);
在这里,前面的都为0,即size=0,后面的表示mode=MeasureSpec.UNSPECIFIED.那这意味着什么呢?
其实,这就是Measure(0,0)啊,大家可以参考下我之前的博客Measure(0,0)到底发生了什么
最后的结果就是,这个TextView的高度会是他自己默认的高度,即包裹文字的高度。
这段代码也是我碰到这个问题后临时充饥的,看后才敢写这篇文章。因为我之前在GridView的onMeasure()打断点的时候,显示的heightMeasureSpec为0,我当时怎么也想不通,测量模式怎么可能是0呢,除非父亲强制给你这么个模式,否则,再怎么也是AT_MOST或者EXACTLY吧。所以,我就决定在ScrollView的测量方法上下手了,果然得以解惑。
ok,现在开始看GridView,先解决第一个问题,为什么不重写只显示第一行子孩子的高度?
通过断点进入GridView的onMeasure(),此时他的heightMeasureSpec为0X0.
所以,会走到这一步

if (heightMode == MeasureSpec.UNSPECIFIED) {
            heightSize = mListPadding.top + mListPadding.bottom + childHeight +
                    getVerticalFadingEdgeLength() * 2;
        }

我们要关注的就是这个heightSize的大小,因为setMeasuredDimension(widthSize, heightSize);
所以,这个heightSize其实就是这个GridView的高度。
在上面有这么段代码

 if (count > 0) {
            final View child = obtainView(0, mIsScrap);
            ...
            childHeight = child.getMeasuredHeight();

突然很开心,这个childHeight就是第一个自孩子的高度,然后通过这个高度又算出了heightSize,即GridView的高度。
再解决第二个问题,为什么像我之前说的重写了GridView的onMeasure()方法后就会好呢?
恩,可能大家已经猜到了,还是要进来看看,重写后,mode=AT_MOST

 if (heightMode == MeasureSpec.AT_MOST) {
            int ourSize =  mListPadding.top + mListPadding.bottom;
            final int numColumns = mNumColumns;
            for (int i = 0; i < count; i += numColumns) {
                ourSize += childHeight;
                if (i + numColumns < count) {
                    ourSize += mVerticalSpacing;
                }
                if (ourSize >= heightSize) {
                    ourSize = heightSize;
                    break;
                }
            }
            heightSize = ourSize;
        }

这段代码的意思就是,把所有的行的高度都加起来作为GridView的高度,之前我们的size=最大整数值
通过这里的if (ourSize >= heightSize)判断之后,会被替换掉。

如果大家有兴趣,还可以尝试这样重写

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,MeasureSpec.EXACTLY);
        super.onMeasure(widthMeasureSpec, expandSpec);

哈哈,滑一整天都不会累啊。
如果大家有什么建议,还请多多交流。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值