Android中onMesure研究

 

当一个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