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

来源:互联网 发布:淘宝好吃的螺蛳粉 编辑:程序博客网 时间:2024/05/22 06:59

本文主要介绍LinearLayout中分隔线Weight的使用方法
涉及到以下几点内容:

  1. 布局绘制过程
  2. 遍历视图
  3. 在onCreate()方法中获取View的宽度和高度
  4. android:measureWithLargestChild使用说明
  5. 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原始宽度为0Button1 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

1 0