当一个Activity取得焦点后就会向Android系统请求绘制它的布局。Android框架会处理这个绘制的过程,一个View的显示,需要先后调用onMeasure,onLayout和onDraw方法。从字面意思理解onMeasure,为计算,测量。上图所示,A,B,C分别表示为三个View,其中,A View包含B View,B View 又包含C View。这三个View在屏幕上显示出来, 会调用每一个View中的onMeasure方法,其调用的顺序是怎样的呢?A ----> B---->C 还是神马呢???先上代码吧。分别为A,B,C三个类源码码。
public class LayoutA extends LinearLayout{
private String TAG = LayoutA.class.getSimpleName();
public LayoutA(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public LayoutA(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// TODO Auto-generated method stub
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
if(widthMode == MeasureSpec.AT_MOST){
Log.e(TAG,"LayoutA onMeasure() called,widthMode = MeasureSpec.AT_MOST,widthSize = " + widthSize + " ,heightSize = " + heightSize);
}
if(widthMode == MeasureSpec.EXACTLY){
Log.e(TAG,"LayoutA onMeasure() called,widthMode = MeasureSpec.EXACTLY,widthSize = " + widthSize + " ,heightSize = " + heightSize);
}
if(widthMode == MeasureSpec.UNSPECIFIED){
Log.e(TAG,"LayoutA onMeasure() called,widthMode = MeasureSpec.UNSPECIFIED,widthSize = " + widthSize + " ,heightSize = " + heightSize);
}
if(heightMode == MeasureSpec.AT_MOST){
}
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
super.onLayout(changed, l, t, r, b);
// Log.e(TAG," LayoutA onLayout() called...");
}
@Override
protected void onDraw(Canvas canvas) {
// TODO Auto-generated method stub
super.onDraw(canvas);
// Log.e(TAG," LayoutA onDraw() called...");
}
}
... ... ... ...
my_main.xml文件为:
<com.example.measuretest.LayoutA xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:gravity="center"
android:background="#ffff0000">
<com.example.measuretest.LayoutB
android:layout_width="300dip"
android:layout_height="300dip"
android:gravity="center"
android:layout_marginTop="40dip"
android:background="#ff00ff00">
<com.example.measuretest.LayoutC
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/hello_world">
</com.example.measuretest.LayoutC>
</com.example.measuretest.LayoutB>
</com.example.measuretest.LayoutA>
类LayoutB与LayoutA相似,LayoutC的代码为:
public class LayoutC extends Button{
private String TAG = LayoutC.class.getSimpleName();
private float mRadius = 4;
public LayoutC(Context context) {
super(context);
// TODO Auto-generated constructor stub
}
public LayoutC(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
... ... ... ... ...
运行后,log信息为:
08-20 21:15:53.338: E/LayoutC(2377): LayoutC onMeasure() called,widthMode = MeasureSpec.AT_MOST,widthSize = 450 ,heightSize = 450
08-20 21:15:53.338: E/LayoutB(2377): LayoutB onMeasure() called,widthMode = MeasureSpec.EXACTLY,widthSize = 450 ,heightSize = 450
08-20 21:15:53.338: E/LayoutA(2377): LayoutA onMeasure() called,widthMode = MeasureSpec.EXACTLY,widthSize = 480 ,heightSize = 778
08-20 21:15:53.370: E/LayoutC(2377): LayoutC onMeasure() called,widthMode = MeasureSpec.AT_MOST,widthSize = 450 ,heightSize = 450
08-20 21:15:53.370: E/LayoutB(2377): LayoutB onMeasure() called,widthMode = MeasureSpec.EXACTLY,widthSize = 450 ,heightSize = 450
08-20 21:15:53.370: E/LayoutA(2377): LayoutA onMeasure() called,widthMode = MeasureSpec.EXACTLY,widthSize = 480 ,heightSize = 778
08-20 21:15:53.424: E/LayoutC(2377): LayoutC onMeasure() called,widthMode = MeasureSpec.AT_MOST,widthSize = 450 ,heightSize = 450
08-20 21:15:53.424: E/LayoutB(2377): LayoutB onMeasure() called,widthMode = MeasureSpec.EXACTLY,widthSize = 450 ,heightSize = 450
08-20 21:15:53.424: E/LayoutA(2377): LayoutA onMeasure() called,widthMode = MeasureSpec.EXACTLY,widthSize = 480 ,heightSize = 778
从log信息显示为:先调用LayoutC 的onMeasure 方法,而后是LayoutB,最后才是调用LayoutA方法的onMeasure方法。
绘制View有两个过程,一个messure过程和一个layout过程。 messure过程只通过measure(int, int)方法中实现的,messure过程会自顶向下遍历Viewtree。在递归过程中,每个View都设置自己的尺寸规格。在measure过程的最后,每个View都已经存储了它自身的大小了。
由于是递归调用,所以当然是从最里层的view开始调用其onMeasure方法,计算其自身大小,然后调用次外层View 的onMeasure方法。也就是说,上图中A,B,C的调用顺序为:C------>B------->A
(关于measure的调用,请参看Google文档:http://developer.android.com/guide/topics/ui/how-android-draws.html)