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>效果:
输出:
我们可以看到测量了两次,绘制了一次,并且只绘制了一个按钮,因为第二个按钮测量出来的宽度为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>效果:
输出:
从输出看出,进行了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。