在实际应用中,经常会碰到非常规的布局要求,比如说在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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

但是实际试过之后就会发现这样做有一个问题,无论ListView的高度怎么设置,都会只显示一行的高度,那是由于ListView的父容器测量模式为UNSPECIFIED的时候,ListView的高度默认为一个item的高度,ListView中源码如下:

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

这样我们就重写ListView的onMeasure方法,来自定义高度:

    /**
* 重写该方法,达到使ListView适应ScrollView的效果
*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

像如上这样重写onMeasure方法,就能让ScrollView嵌套中的ListView完全填满。仔细看,会发现上面的代码很奇怪,下面解释下其原理,不想了解的可以跳过了。

原理分析:

查看上面的代码我们发现我们把高度写成了一个固定值expandSpec ,这个值是这样计算出来的

expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST);
  • 1
  • 1

从上面代码可以看出,把整数类型的最大值右移了2位,作为size传入,另外把一个AT_MOST的常量作为mode传入。

为什么要传入这样一个奇葩的参数呢,这就Android的实体测量机制有关了,android中规定,测量的值(高度或宽度)为一个int类型,但不是普通的int,而是一个进过处理的int,在view视图中我们制定一个高度需要2个参数,1个是具体的值,一个是测量模式,测量模式就是我们在布局中经常用到的MATCH_PARENT 、WRAP_CONTENT。他们是一个int型的常量,对应的值分别是:

LayoutParams.MATCH_PARENT  对应 MeasureSpec.EXACTLY
LayoutParams.WRAP_CONTENT 对应 MeasureSpec.AT_MOST
  • 1
  • 2
  • 1
  • 2

而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
  • 3
  • 4
  • 1
  • 2
  • 3
  • 4

可以看到,分别是把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次方)

ScrollView嵌套ListView,ListView完全展开及makeMeasureSpec测量机制原理分析的更多相关文章

  1. ScrollView嵌套使用ListView冲突的解决与分析

    因为ScrollView与ListView都是具有滚动条的控件,所以嵌套在一起使用的时候可能会出现事件的冲突,比如我就遇见了ListView中只显示一条数据的问题.解决的办法,就是自定义了一个List ...

  2. Android Scrollview嵌套下listView动态加载数据,解决onScrollChanged执行多次数据重复问题

    这一篇博客和上一篇讲的都是listView的动态加载,但有所不同的是,本篇的listView是嵌套在ScrollView下的,有时候在一个Activity中可能分为好几个模块,由于展示的需要(手机屏幕 ...

  3. ScrollView嵌套ListView,GridView数据加载不全问题的解决

    我们大家都知道ListView,GridView加载数据项,如果数据项过多时,就会显示滚动条.ScrollView组件里面只能包含一个组件,当ScrollView里面嵌套listView,GridVi ...

  4. 关于ScrollView嵌套ListView问题

    Android开发之ScrollView中嵌套ListView的解决方案   原文:http://blog.csdn.net/minimicall/article/details/40983331   ...

  5. Android之ScrollView嵌套ListView和GridView冲突

    由于ListView,GridView本身都继承于ScrollView,一旦在ScrollView中嵌套ScrollView, 在ScrollView中嵌套使用ListView或者GridView,L ...

  6. 四种方案解决ScrollView嵌套ListView问题(转)

    以下文章转自@安卓泡面 在工作中,曾多次碰到ScrollView嵌套ListView的问题,网上的解决方法有很多种,但是杂而不全.我试过很多种方法,它们各有利弊. 在这里我将会从使用ScrollVie ...

  7. Android之ScrollView嵌套ListView冲突

    在ScrollView中嵌套使用ListView,ListView只会显示一行多一点.两者进行嵌套,即会发生冲突.由于ListView本身都继承于ScrollView,一旦在ScrollView中嵌套 ...

  8. 冲突--ScrollView嵌套ListView冲突问题的最优解决方案

    项目做多了之后,会发现其实 ScrollView嵌套ListVew或者GridView等很常用,但是你也会发现各种奇怪问题产生.根据个人经验现在列出常见问题以及代码最少最简单的解决方法. 问题一 :  ...

  9. android 解决ScrollView嵌套ListView的问题,不能全屏,全屏不能显示下面控件

    在开发中遇到ScrollView嵌套ListView的问题,最开始发出不能全屏,效果是这样的: 但我想要的效果是这样的: 下面看一下布局文件: <?xml version="1.0&q ...

随机推荐

  1. delphi 消息的使用

    //分析结果 WM_AnalysisResult = WM_USER + 1009; SendMessage(G_MainHandle, WM_AnalysisResult, 0, 0); proce ...

  2. 在Visual Studio中使用序列图描述对象之间的互动

    当需要描述多个对象之间的互动,可以考虑使用序列图. 在建模项目下添加一个名称为"Basic Flow"的序列图. 比如描述客户是如何在MVC下获取到视图信息的. 备注: ● 通常是 ...

  3. 连接查询简析 join 、 left join 、 right join

    join :取两个表的合集: left join:左表的数据全部保留,然后增加右表与左表条件匹配的记录.如下 select cc.* from cloud_groups as cg left join ...

  4. python测试开发django-18.admin后台中文版

    前言 django的admin后台页面默认是英文的,不喜欢英文的话,可以改下setting.py里面的语言设置,改成中文版的显示 语言设置 打开setting.py,找到以下内容 # Internat ...

  5. HTML5+Java(Spring下) 拍照上传图片

    使用支持html5的浏览器,找个有摄像头,再建一个文件接收base64字串的图片然后保存就哦了 <html> <head runat="ReYo-Server"& ...

  6. NSObject的hash方法

    NSObject的hash方法 说明 本示例仅仅演示一个对象什么时候执行hash方法. 细节 1. 必要的Model类,重载了hash方法用以反映Hash方法是否被调用了 2. 测试 // // Vi ...

  7. 实习医生格蕾第一季/全集Grey’s Anatomy迅雷下载

    第一季 Grey's Anatomy Season 1 (2005)看点:欢迎来到格雷斯医院,哈佛医学院西区最艰苦的外科住院医实习程序,医务新人最残酷的训练场.Meredith.Izzie和Chris ...

  8. jdgui反编译+javac编译=无源文件改动代码

    首先我们要知道,打包好的Java程序中都是编译好的字节码文件(*.class).这些class文件会在执行的时候被载入到JVM中. 若想替换掉某一个类,那么仅仅须要将该类的源代码又一次编译然后再替换之 ...

  9. SqlServer 还原数据库 代码实现

    RESTORE DATABASE TargetDB FROM DISK = 'D:\DataBase\DB.bak' with replace, MOVE 'DB' TO 'D:\DataBase\T ...

  10. yolo源码解析(三)

    七 测试网络 模型测试包含于test.py文件,Detector类的image_detector()函数用于检测目标. import os import cv2 import argparse imp ...