您的位置:首页 > 移动开发 > Android开发

安卓开发学习之005 LinearLayout之Weight/measureWithLargestChild详解

2015-10-26 09:44 731 查看
本文主要介绍LinearLayout中分隔线Weight的使用方法

涉及到以下几点内容:

布局绘制过程

遍历视图

在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_content608480
BUTTON1234567890wrap_content152152
A11wrap_content15246
A22wrap_content1522
NO00wrap_content8888
linearLayout_true2——wrap_content448448
BUTTON12340wrap_content112112
W11wrap_content112112
W22wrap_content112112
NO10wrap_content8888
linearLayout_false——wrap_content416416
BUTTON1234567890wrap_content152152
W31wrap_content8888
W42wrap_content8888
NO20wrap_content8888
linearLayout3——match_parent608480
BUTTON10wrap_content8888
BUTTON21.0010876
BUTTON31.0wrap_content196164
BUTTON42.00216152
linearLayout41match_parent608480
1/2 WIDTH0.50304240
结论:

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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息