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

本文主要介绍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"
              >
    

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="linearLayout_measureWithLargestChild_true1"/>

    
    
    <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"/>

    
    <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"/>
    
    <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"/>

    
    <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"/>

    
    <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上运行后结果图

安卓开发学习之005 LinearLayout之Weight/measureWithLargestChild详解_第1张图片

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

你可能感兴趣的:(android)