引言
最近同学问了一个问题,就是ScrollView嵌套ListView时出现了冲突。上网也查阅了一些资料,各路大神也是各显神通。本着“万事以简为要”的原则,最终找到了一个超级简单而且实用的解决方法——重写ListView的onMeasure()方法。
分析
具体怎么实现呢?容我慢慢道来。
首先可以看出在onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法中传递进去了2个int参数,但其并不是简单的一个int变量。从源码中发现,它们各自都包括了2个属性,分别为SIZE和MODE。
以下为源码:
<span style="font-family:Times New Roman;">
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
</span>
从中可以看出上面的2个参数就是通过MeasureSpec.makeMeasureSpec(SIZE,MODE)得到的。其size为自定义的宽度/高度大小,Mode为测量的模式。
先看下表,然后我们来说说这个MODE究竟为何方神圣。
父控件对子控件无限制,大小可在布局文件自定义 |
|
父控件已明确子控件大小,子控件无条件服从 |
|
子控件可根据自己的实际需要自适应大小 |
Ps:默认使用的时第一种MODE。
由上表可以看出,onMeasure()传入的2个参数中,SIZE即为控件的大小,然而并不能就此说明该控件内容的大小刚好为所提供的SIZE这么大,因为他还需要经过MODE来计算。也就是说(以高度为例),
ActualSize=f(SIZE)
其中:ActualSize为控件内容实际所占高度;
f()为MODE的测量模式,即MeasureSpec.getMode(heightMeasureSpec);
SIZE为传入的SIZE,即MeasureSpec.getSize(heightMeasureSpec)。
相信看到这里,大伙都应该知道下一步我们该做了什么了——重写onMeasure()。
代码如下:
<span style="font-family:Times New Roman;">
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(SIZE,MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
</span>
其中SIZE可以自己写,就是你预设定的最大高度。(一般就用Integer.MAX_VALUE>>2-1,也就是(1<<30)-1。至于为什么使用这个值,那是因为MeasureSpec的低30位才表示size,最大也就是(1<<30)-1),高2位表示的是SpecMode)。
这样就可以完美而简单高效地解决ScrollView内嵌ListView的冲突问题。
结束语
第一次发博客,排版亟待加强,日后会改善,这次多多见谅了。同时发现看源码原来是这么好玩的一件事,继续努力。
最后,有兴趣的还可以去关注下GridView、ExpandableListView的onMeassure()方法,我相信你的收获不会只有一点点的。