android开发游记:ScrollView嵌套ListView,ListView完全展开及makeMeasureSpec测量机制原理分析
来源:互联网 发布:百度云网络连接失败 编辑:程序博客网 时间:2024/05/22 03:38
在实际应用中,经常会碰到非常规的布局要求,比如说在ScrollView里嵌套ListView,ScrollView和ListView都是可以滚动的控件,这样布局看似很奇怪,但是有些效果又不得不这样做。比如说:一个长布局中有部分是列表格式,布局长度又超过屏幕高度,这样的情况就得使用这种布局了。
<ScrollView android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentLeft="true" android:fillViewport="true"> <LinearLayout android:id="@+id/showin" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <ListView android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white"/> </LinearLayout> </ScrollView>
但是实际试过之后就会发现这样做有一个问题,无论ListView的高度怎么设置,都会只显示一行的高度,那是由于ListView的父容器测量模式为UNSPECIFIED的时候,ListView的高度默认为一个item的高度,ListView中源码如下:
if (heightMode == MeasureSpec.UNSPECIFIED) { heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2;}
这样我们就重写ListView的onMeasure方法,来自定义高度:
/** * 重写该方法,达到使ListView适应ScrollView的效果 */ @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); }
像如上这样重写onMeasure方法,就能让ScrollView嵌套中的ListView完全填满。仔细看,会发现上面的代码很奇怪,下面解释下其原理,不想了解的可以跳过了。
原理分析:
查看上面的代码我们发现我们把高度写成了一个固定值expandSpec ,这个值是这样计算出来的
expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
从上面代码可以看出,把整数类型的最大值右移了2位,作为size传入,另外把一个AT_MOST的常量作为mode传入。
为什么要传入这样一个奇葩的参数呢,这就android的实体测量机制有关了,android中规定,测量的值(高度或宽度)为一个int类型,但不是普通的int,而是一个进过处理的int,在view视图中我们制定一个高度需要2个参数,1个是具体的值,一个是测量模式,测量模式就是我们在布局中经常用到的MATCH_PARENT 、WRAP_CONTENT。他们是一个int型的常量,对应的值分别是:
LayoutParams.MATCH_PARENT 对应 MeasureSpec.EXACTLYLayoutParams.WRAP_CONTENT 对应 MeasureSpec.AT_MOST
而EXACTLY和AT_MOST的值是:
private static final int MODE_SHIFT = 30; public static final int EXACTLY = 1 << MODE_SHIFT; //填满父控件高度 public static final int AT_MOST = 2 << MODE_SHIFT; //自适应当前控件高度
可以看到,分别是把1和2左移30位的值。为什么会这样呢?
android中把测量出的int做了处理,int的长度时32位,把前2位作为标志位标示了测量模式,如EXACTLY、AT_MOST
把后30位作为测量的具体高度或宽度。
也就是说,把一个int分成了2部分,使一个int值同时拥有了模式和具体数值的2部分信息!
EXACTLY的值是1向左进位30,就是01 00000000000…(01后跟30个0)
AT_MOST的值是2向左进位30,就是10 00000000000…(10后跟30个0)
所以我们在调用MeasureSpec.makeMeasureSpec(size,mode)方法时,传入的size参数要把Integer.MAX_VALUE右移2位,因为前两位会被认为是标志,而不是值。这样我们传入的参数才会被认为是最大的int类型的值,同时传入AT_MOST作为模式,那么前两位就会被赋值为10,那么我们来实际计算一下,我们调用MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST)
的实际结果是多少:
int类型长度是32位,其中包含1个标准位,所有最大值Integer.MAX_VALUE为 2的31次方 :2147483648
16进制下为0x7fffffff,二进制为:01 11111111111…(01后跟30个1)
由于前两位在android测量基础下是无效的标志位,所以我们右移2位的结果为000111111111111…
调用makeMeasureSpec方法后会把前两位替换为AT_MOST的前两位(其实是直接相加)
结果为:100111111111… 十六进制:9fffffff,十进制:-1610612737所以你调试的时候后发现,返回的值为-1610612737 ,就是这个原因,因为前2位是无效的标志位,所以其实这个数所代表的最大值是 2的29次方:536870912。所以对于android测量机制来理解我们传入的-1610612737,是这样理解的:
-1610612737 代表: 测量模式为AT_MOST,最大高度为536870912(2的29次方)
- android开发游记:ScrollView嵌套ListView,ListView完全展开及makeMeasureSpec测量机制原理分析
- android ScrollView嵌套ListView的方法分析
- android ScrollView嵌套ListView
- android ScrollView嵌套ListView
- android ScrollView嵌套Listview
- android scrollview 嵌套listview
- Android ScrollView嵌套ListView
- Android应用开发-ScrollView嵌套ListView
- android开发中使用scrollView嵌套Listview
- [Android开发]ScrollView中嵌套ListView
- ScrollView嵌套全部展开显示的ListView
- ScrollView中嵌套ListView分析
- Android之ScrollView嵌套ListView
- Android之ScrollView嵌套ListView
- android scrollView嵌套listview问题
- Android中ScrollView嵌套ListView
- Android之ScrollView嵌套ListView
- Android ScrollView中嵌套ListView
- 八大排序算法
- apache 301跳转
- Android广播机制
- 在线日志状态含义
- 为知笔记 VS 有道笔记
- android开发游记:ScrollView嵌套ListView,ListView完全展开及makeMeasureSpec测量机制原理分析
- APP安全之SQL注入之什么是SQL注入(一)
- 解决集成支付宝报一堆warning: (arm64) /Users/scmbuild/workspace/standard-pay/.....警告问题
- php扔骰子举例
- 中文转码问题总结
- PHP字符串解释成时间,加一天,加一个月等
- Ubuntu Server 14.04.3 安装
- android 开源项目集合
- iOS里面尽量的避免两件事情的耦合性--ui刷新不应该和任何的事情耦合