安卓开发学习之005 LinearLayout之Weight/measureWithLargestChild详解
2015-10-26 09:44
731 查看
本文主要介绍LinearLayout中分隔线Weight的使用方法
涉及到以下几点内容:
布局绘制过程
遍历视图
在onCreate()方法中获取View的宽度和高度
android:measureWithLargestChild使用说明
weight及weightSum使用说明
Android开发文档中有如下描述:
“绘制布局由两个遍历过程组成:测量过程和布局过程。测量过程由measure(int,int)方法完成,该方法从上到下遍历视图树。在递归遍历过程中,每个视图都会向下层传递尺寸和规格。当measure方法遍历结束时,每个视图都保存了各自的尺寸信息。第二个过程由layout(int,int,int,int)方法完成,该方法也是由上而下遍历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所有子视图的位置信息。”
1.测量高度、宽度
在测量过程中得出的高度和宽度,对应getMeasuredWidth()方法
2.实际高度、宽度
布局完成后得到的高度和宽度,对应getWidth()方法
两者区别:
getWidth(): View在布局完成后整个View的实际宽度。
getMeasuredWidth(): 对View上的内容进行测量后得到的View占据的宽度,有可能大于实际宽度
值得注意的陷进:如果在Activity的onCreate()或者Fragment的onCreateView()中直接调用View的getHeight和getWidth方法,会发现返回值都是0.
首先分析为什么在onCreate()方法中读取视图的尺寸会返回0.当onCreate()方法被调用时,会通过LayoutInflater将XML布局文件填充到ContenView。填充过程只包括创建视图,却不包括设置其大小。那么,视图的大小是在何时指定的呢?
通过“布局绘制过程”可以得出如下结论:
只有在整个布局绘制完毕后,视图才能得到自身的高和宽,这个过程发生在onCreate()方法之后,因此,在此之前调用getHeight()和getWidth()方法返回的结果都是0.
那么如何在onCreate()阶段得到View的宽度和高度呢?
可以使用View的post()方法。该方法接收一个Runnable线程参数,并将其添加到消息队列中。有趣的是Runnable线程会在UI线程中执行。
方法如下:
xml属性 : android:measureWithLargestChild;
设置方法 : setMeasureWithLargestChildEnable(boolean b);
作用 : 该属性为true的时候, 所有带权重的子元素都会具有最大子元素的最小尺寸;
默认为false
其中包含5个子LinearLayout
第一个LinearLayout 设置measureWithLargestChild=true 并且子视图总测量宽度>屏幕实际宽度(480x800分辨率)
第二个LinearLayout设置measureWithLargestChild=true 并且子视图总测量宽度<屏幕实际宽度(480x800分辨率)
第三个LinearLayout设置measureWithLargestChild=false
第四个LinearLayout主要是weight的使用
第五个LinearLayout是weight配合weightSum使用
最后还有一个自定义LogTextView用来显示各个子视图的测量宽度和布局完成后实际宽度
结论:
1. 测量过程发生在onCreateView()阶段,在onStart()方法之后,视图才能得到自身的实际高和宽
2. 如果layout_width 不是“wrap_content ”则measureWithLargestChild不起作用(这个没在此表格中体现出来,读者可以自己测试)
3. 当measureWithLargestChild=true 并且子视图总测量宽度>屏幕实际宽度时,所有带权重(weight)的子元素都会具有最大子元素的测量宽度,但带权重的子元素最后实际宽度却不是,会出现布局异常;并且LinearLayout的实际宽度=屏幕最大宽度(这里是480)
4. 当measureWithLargestChild=true 并且子视图总测量宽度<屏幕实际宽度时,所有带权重(weight)的子元素都会具有最大子元素的测量宽度和实际宽度;并且LinearLayout的实际宽度=最大子元素的宽度*子元素个数(这里是112*4=448)
5. 当measureWithLargestChild=false时,不受以上约束
6. 当父layout_width =“wrap_content “时,weight属性不起作用(由linearLayout_false 得出此结论)
7. Button宽度计算公式:
原始宽度+权重*父视图剩余空间/权重和
7.1 未指定android:weightSum属性时,权重和=所有子控件的weight之和,weight未指定时为0
7.2如果指定了android:weightSum属性,权重和=android:weightSum指定的值。不管子控件weight和是多少
7.3weight是对剩余空间的分配而不是对LinearLayout空间的分配
开发工具:Android Studio1.4
SDK: Android 6.0
API 23
代码下载:Weight.zip
涉及到以下几点内容:
布局绘制过程
遍历视图
在onCreate()方法中获取View的宽度和高度
android:measureWithLargestChild使用说明
weight及weightSum使用说明
1.布局绘制过程
在讲解measureWithLargestChild和weight使用方法之前必须先来简单了解下布局的绘制过程。Android开发文档中有如下描述:
“绘制布局由两个遍历过程组成:测量过程和布局过程。测量过程由measure(int,int)方法完成,该方法从上到下遍历视图树。在递归遍历过程中,每个视图都会向下层传递尺寸和规格。当measure方法遍历结束时,每个视图都保存了各自的尺寸信息。第二个过程由layout(int,int,int,int)方法完成,该方法也是由上而下遍历视图树,在遍历过程中,每个父视图通过测量过程的结果定位所有子视图的位置信息。”
2.如何遍历视图
/** *采用递归方法遍历所有view * * @param viewGroup */ public void traversalView(ViewGroup viewGroup) { //求当前ViewGroup下子视图的总数量 int count = viewGroup.getChildCount(); for (int i = 0; i < count; i++) { //获取第I个子视图 View view = viewGroup.getChildAt(i); //如果子视图属于ViewGroup,有可能其下仍然含有子视图,继续判断 if (view instanceof ViewGroup) { traversalView((ViewGroup) view); } else { doView(view); } } } /** * 处理view * * * @param view */ private void doView(View view) { //TODO:something }
3.在onCreate()方法中获取View的宽度和高度
这里有两个概念:1.测量高度、宽度
在测量过程中得出的高度和宽度,对应getMeasuredWidth()方法
2.实际高度、宽度
布局完成后得到的高度和宽度,对应getWidth()方法
两者区别:
getWidth(): View在布局完成后整个View的实际宽度。
getMeasuredWidth(): 对View上的内容进行测量后得到的View占据的宽度,有可能大于实际宽度
值得注意的陷进:如果在Activity的onCreate()或者Fragment的onCreateView()中直接调用View的getHeight和getWidth方法,会发现返回值都是0.
首先分析为什么在onCreate()方法中读取视图的尺寸会返回0.当onCreate()方法被调用时,会通过LayoutInflater将XML布局文件填充到ContenView。填充过程只包括创建视图,却不包括设置其大小。那么,视图的大小是在何时指定的呢?
通过“布局绘制过程”可以得出如下结论:
只有在整个布局绘制完毕后,视图才能得到自身的高和宽,这个过程发生在onCreate()方法之后,因此,在此之前调用getHeight()和getWidth()方法返回的结果都是0.
那么如何在onCreate()阶段得到View的宽度和高度呢?
可以使用View的post()方法。该方法接收一个Runnable线程参数,并将其添加到消息队列中。有趣的是Runnable线程会在UI线程中执行。
方法如下:
view.post(new Runnable() { @Override public void run() { System.out.println("RealWidth=" + view.getWidth()); } });
4.measureWithLargestChild和weight使用说明
官方API:xml属性 : android:measureWithLargestChild;
设置方法 : setMeasureWithLargestChildEnable(boolean b);
作用 : 该属性为true的时候, 所有带权重的子元素都会具有最大子元素的最小尺寸;
默认为false
4.1先看下布局文件
res/layout/fragment_weight.xml<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/linearlayout" android:tag="linearLayout_parent" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" tools:context=".WeightActivityFragment" tools:showIn="@layout/activity_weight" > <!--android:measureWithLargestChild作用 : 该属性为true的时候, 所有带权重的子元素都会具有最大子元素的最小尺寸; 且只有当父view布局方向上的宽度或高度为wrap_content才有效--> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="linearLayout_measureWithLargestChild_true1"/> <!-- 如果layout_width=“match_parent 则measureWithLargestChild不起作用”--> <!-- measureWithLargestChild=true 并且子视图总测量宽度>屏幕实际宽度(480x800分辨率)--> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:measureWithLargestChild="true" android:tag="linearLayout_measureWithLargestChild_true1" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button123456789" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0" android:text="A1" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="2.0" android:text="A2" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="no0" /> </LinearLayout> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="linearLayout_measureWithLargestChild_true2"/> <!-- measureWithLargestChild=true 子视图总测量宽度<屏幕实际宽度(480x800分辨率)--> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:measureWithLargestChild="true" android:tag="linearLayout_measureWithLargestChild_true2" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button1234" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0" android:text="w1" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="2.0" android:text="w2" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="no1" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="@dimen/divider_margin" android:background="#f00"/> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:text="measureWithLargestChild=false"/> <!-- measureWithLargestChild=false--> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:measureWithLargestChild="false" android:tag="linearLayout_measureWithLargestChild_false"> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button123456789" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1.0" android:text="w3" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="2.0" android:text="w4" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="no2" /> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="@dimen/divider_margin" android:background="#f00"/> <!-- weight使用--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="linearLayout3" > <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button1"/> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1.0" android:text="Button2" /> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button3" android:layout_weight="1.0" /> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2.0" android:text="Button4"/> </LinearLayout> <View android:layout_width="match_parent" android:layout_height="@dimen/divider_margin" android:background="#f00"/> <!-- weight配合weightSum使用--> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:tag="linearLayout4" android:gravity="center" android:weightSum="1.0"> <Button android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="0.5" android:text="1/2 width" /> </LinearLayout> <com.antex.weight.LogTextBox android:id="@+id/textView1" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@drawable/box" android:scrollbars="vertical" android:textColor="#f0f"/> </LinearLayout>
其中包含5个子LinearLayout
第一个LinearLayout 设置measureWithLargestChild=true 并且子视图总测量宽度>屏幕实际宽度(480x800分辨率)
第二个LinearLayout设置measureWithLargestChild=true 并且子视图总测量宽度<屏幕实际宽度(480x800分辨率)
第三个LinearLayout设置measureWithLargestChild=false
第四个LinearLayout主要是weight的使用
第五个LinearLayout是weight配合weightSum使用
最后还有一个自定义LogTextView用来显示各个子视图的测量宽度和布局完成后实际宽度
4.2java代码
package com.antex.weight; import android.os.Bundle; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.LinearLayout; /** * A placeholder fragment containing a simple view. */ public class WeightActivityFragment extends Fragment { private LogTextBox textView; public WeightActivityFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_weight, container, false); textView = (LogTextBox) view.findViewById(R.id.textView1); LinearLayout layout = (LinearLayout) view.findViewById(R.id.linearlayout); textView.append("WeightActivityFragment.onCreateView\n"); //调用测量方法, 调用了该方法之后才能通过getMeasuredWidth()等方法获取宽高 layout.measure(0, 0); traversalView(layout); return view; } @Override public void onStart() { super.onStart(); textView.append("\n\nWeightActivityFragment.onStart\n\n"); } /** *采用递归方法遍历所有view * * @param viewGroup */ public void traversalView(ViewGroup viewGroup) { //如果是LinearLayout 输出其宽 if(viewGroup instanceof LinearLayout) doView(viewGroup); //求当前ViewGroup下子视图的总数量 int count = viewGroup.getChildCount(); for (int i = 0; i < count; i++) { //获取第I个子视图 View view = viewGroup.getChildAt(i); //如果子视图属于ViewGroup,有可能其下仍然含有子视图,继续判断 if (view instanceof ViewGroup) { traversalView((ViewGroup) view); } else { doView(view); } } } /** * 处理view * getMeasuredWidth()和getWidth()区别 * getWidth(): View在布局完成后整个View的实际宽度。 * getMeasuredWidth(): 对View上的内容进行测量后得到的View占据的宽度,有可能大于实际宽度 * * * @param view */ private void doView(final View view) { if(view instanceof Button) { textView.append(((Button) view).getText().toString().toUpperCase() + " MeasuredWidth=" + view.getMeasuredWidth() + "\n"); view.post(new Runnable() { @Override public void run() { textView.append(((Button) view).getText().toString().toUpperCase() + " RealWidth=" + view.getWidth() + "\n"); } }); } else if (view instanceof LinearLayout) { textView.append(view.getTag()+" MeasuredWidth="+view.getMeasuredWidth()+"\n"); //利用View的post()方法求出得View的宽度 //如果直接使用getWidth()方法,返回的结果是0 view.post(new Runnable() { @Override public void run() { textView.append(view.getTag()+" RealWidth=" + view.getWidth() + "\n"); } }); } } }
4.3上运行后结果图
4.4最后文本输出内容如下
屏幕分辨率: 480x800 mdpi 输出结果为 WeightActivityFragment.onCreateView linearLayout_parent MeasuredWidth=608 linearLayout_measureWithLargestChild_true1 MeasuredWidth=608 BUTTON123456789 MeasuredWidth=152 A1 MeasuredWidth=152 A2 MeasuredWidth=152 NO0 MeasuredWidth=88 linearLayout_measureWithLargestChild_true2 MeasuredWidth=448 BUTTON1234 MeasuredWidth=112 W1 MeasuredWidth=112 W2 MeasuredWidth=112 NO1 MeasuredWidth=88 linearLayout_measureWithLargestChild_false MeasuredWidth=416 BUTTON123456789 MeasuredWidth=152 W3 MeasuredWidth=88 W4 MeasuredWidth=88 NO2 MeasuredWidth=88 linearLayout3 MeasuredWidth=608 BUTTON1 MeasuredWidth=88 BUTTON2 MeasuredWidth=108 BUTTON3 MeasuredWidth=196 BUTTON4 MeasuredWidth=216 linearLayout4 MeasuredWidth=608 1/2 WIDTH MeasuredWidth=304 WeightActivityFragment.onStart linearLayout_parent RealWidth=480 linearLayout_measureWithLargestChild_true1 RealWidth=480 BUTTON123456789 RealWidth=152 A1 RealWidth=46 A2 RealWidth=2 NO0 RealWidth=88 linearLayout_measureWithLargestChild_true2 RealWidth=448 BUTTON1234 RealWidth=112 W1 RealWidth=112 W2 RealWidth=112 NO1 RealWidth=88 linearLayout_measureWithLargestChild_false RealWidth=416 BUTTON123456789 RealWidth=152 W3 RealWidth=88 W4 RealWidth=88 NO2 RealWidth=88 linearLayout3 RealWidth=480 BUTTON1 RealWidth=88 BUTTON2 RealWidth=76 BUTTON3 RealWidth=164 BUTTON4 RealWidth=152 linearLayout4 RealWidth=480 1/2 WIDTH RealWidth=240
4.5输出结果分析
根据布局及输出数据可以得出如下表格数据名称 | 权重(或总权重) | layout_width | 测量宽度 | 实际宽度 |
---|---|---|---|---|
LinearLayout_true1 | —— | wrap_content | 608 | 480 |
BUTTON123456789 | 0 | wrap_content | 152 | 152 |
A1 | 1 | wrap_content | 152 | 46 |
A2 | 2 | wrap_content | 152 | 2 |
NO0 | 0 | wrap_content | 88 | 88 |
linearLayout_true2 | —— | wrap_content | 448 | 448 |
BUTTON1234 | 0 | wrap_content | 112 | 112 |
W1 | 1 | wrap_content | 112 | 112 |
W2 | 2 | wrap_content | 112 | 112 |
NO1 | 0 | wrap_content | 88 | 88 |
linearLayout_false | —— | wrap_content | 416 | 416 |
BUTTON123456789 | 0 | wrap_content | 152 | 152 |
W3 | 1 | wrap_content | 88 | 88 |
W4 | 2 | wrap_content | 88 | 88 |
NO2 | 0 | wrap_content | 88 | 88 |
linearLayout3 | —— | match_parent | 608 | 480 |
BUTTON1 | 0 | wrap_content | 88 | 88 |
BUTTON2 | 1.0 | 0 | 108 | 76 |
BUTTON3 | 1.0 | wrap_content | 196 | 164 |
BUTTON4 | 2.0 | 0 | 216 | 152 |
linearLayout4 | 1 | match_parent | 608 | 480 |
1/2 WIDTH | 0.5 | 0 | 304 | 240 |
1. 测量过程发生在onCreateView()阶段,在onStart()方法之后,视图才能得到自身的实际高和宽
2. 如果layout_width 不是“wrap_content ”则measureWithLargestChild不起作用(这个没在此表格中体现出来,读者可以自己测试)
3. 当measureWithLargestChild=true 并且子视图总测量宽度>屏幕实际宽度时,所有带权重(weight)的子元素都会具有最大子元素的测量宽度,但带权重的子元素最后实际宽度却不是,会出现布局异常;并且LinearLayout的实际宽度=屏幕最大宽度(这里是480)
4. 当measureWithLargestChild=true 并且子视图总测量宽度<屏幕实际宽度时,所有带权重(weight)的子元素都会具有最大子元素的测量宽度和实际宽度;并且LinearLayout的实际宽度=最大子元素的宽度*子元素个数(这里是112*4=448)
5. 当measureWithLargestChild=false时,不受以上约束
6. 当父layout_width =“wrap_content “时,weight属性不起作用(由linearLayout_false 得出此结论)
7. Button宽度计算公式:
原始宽度+权重*父视图剩余空间/权重和
7.1 未指定android:weightSum属性时,权重和=所有子控件的weight之和,weight未指定时为0
7.2如果指定了android:weightSum属性,权重和=android:weightSum指定的值。不管子控件weight和是多少
7.3weight是对剩余空间的分配而不是对LinearLayout空间的分配
我们用上面表格中的数据来验证下: linearLayout3 中Button1和Button3原始宽度为wrap_content可得知 原始宽度为88,Button2和Button4原始宽度为0 Button1 weidth=88=88+0*(480-88-88)/(0+1.0+1.0+2.0) Button2 weidth=76=0+1.0*(480-88-88)/(0+1.0+1.0+2.0) Button3 weidth=164=88+1.0*(480-88-88)/(0+1.0+1.0+2.0) Button4 weidth=152=0+2.0*(480-88-88)/(0+1.0+1.0+2.0) linearLayout4中 1/2 WIDTH weidth=240=0+0.5*480/1.0
开发工具:Android Studio1.4
SDK: Android 6.0
API 23
代码下载:Weight.zip
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题