注: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);
哈哈,滑一整天都不会累啊。
如果大家有什么建议,还请多多交流。