android之layout_width/layout_height和weight

android的控件从初始化到显示到屏幕上,在绘制(Draw)之前,需要测量(Measure)其高度和宽度。下面以Button为例,用实验证明。

重写Button类:

package com.zzj.ui.control;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.widget.Button;

public class MyButton extends Button {

	public MyButton(Context context) {
		this(context, null);
	}
	public MyButton(Context context, AttributeSet attrs) {
        this(context, attrs, android.R.attr.buttonStyle);
    }

	public MyButton(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
	}
	
	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		System.out.println("width:" + this.getWidth());
		System.out.println("height:" + this.getHeight());
		System.out.println("MyButton" + this.getId() + ".onMeasure() is called in the time " + System.currentTimeMillis() +"!");
	}
	
	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		System.out.println("width:" + this.getWidth());
		System.out.println("height:" + this.getHeight());
		System.out.println("MyButton" + this.getId() + ".onDraw() is called in the time " + System.currentTimeMillis() +"!");
	}

}
布局文件:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <com.zzj.ui.control.MyButton
        android:id="@+id/wbutton1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="b1" />

    <com.zzj.ui.control.MyButton
        android:id="@+id/wbutton2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="b2" />

</LinearLayout>
效果:

android之layout_width/layout_height和weight_第1张图片

输出:

android之layout_width/layout_height和weight_第2张图片

我们可以看到测量了两次,绘制了一次,并且只绘制了一个按钮,因为第二个按钮测量出来的宽度为0,所以就没必要绘制了。实验证明,在绘制前确实是先测量。

修改一下布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" >

    <com.zzj.ui.control.MyButton
        android:id="@+id/wbutton1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="2"
        android:text="b1" />

    <com.zzj.ui.control.MyButton
        android:id="@+id/wbutton2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:text="b2" />

</LinearLayout>
效果:

android之layout_width/layout_height和weight_第3张图片

输出:

android之layout_width/layout_height和weight_第4张图片

从输出看出,进行了4次测量,可见控件会根据初始条件进行多次测量。

那么测量的公式是什么呢?

首先确认以下3点:

1.只有定义在LinearLayout中的控件,设置的layout_weight才有效。

2.当LinearLayout的android:orientation属性为horizontal(水平)时,layout_weight只对width有效。

3.当LinearLayout的android:orientation属性为vertical(垂直)时,layout_weight只对height有效。

设布局控件为LinearLayout,其宽度为X,设置布局控件的android:orientation属性为horizontal(水平),在布局控件中放置n个Button,每个Button命名为b1、b2、...、bn,每个Button的宽度width定义为w1、w2、...、wn,权重weight定义为g1、g2、...、gn,则按钮bi最终的宽度为:

wi + gi / (g1 + g2 + ... + gn) * (X - (w1 + w2 + ... + wn))

公式计算分两步:

1.测量bi通过layout_width设置的宽度(也就是wi)。

2.测量bi通过layout_weight设置的宽度。按钮bi通过weight设置的宽度等于bi的权重比(gi / (g1 + g2 + ... + gn))乘以屏幕的剩余宽度(X - (w1 + w2 + ... + wn))。

最终结果就是将两次测量的结果相加。

特别地,当bi的宽度layout_width设置为match_parent(fill_parent)时,wi=X。屏幕的剩余宽度可能为负数(X < (w1 + w2 + ... + wn)),所以会出现权重比越大的按钮最终宽度越小的现象。

需要注意的是,测量和绘制都是有先后顺序的,在布局文件中先定义的控件先测量或绘制。最后一次计算,前面控件宽度的总和如果超过了屏幕的宽度,则后面控件的宽度都为0,并且宽度为0的控件都不会被绘制。

系统默认的weight为0,如果不设置weight(g1 + g2 + ... + gn = 0),则无法进行第二次计算(分母为0)。

建议:如果要使用weight属性,使各个控件按设置的weight显示,显然应该设置width为0。


你可能感兴趣的:(android之layout_width/layout_height和weight)