注:
1、爲了方便描述,假定現在有三層佈局,分別爲:最外層View1、第二層View2、第三層View3;
2、下面“測量子控件、佈局子控件、繪製子控件”的思路都是:用for循環遍歷子控件,調用子控件的measure()、layout()、draw()方法。下面就不對此進行解釋。
測量
measure(int widthMeasureSpec, int heightMeasureSpec){}
1、該方法在View類中,所有子類都是調用這個方法;
2、參數是自身的MeasureSpec,由父控件計算後傳遞過來;
3、該方法做2件事:
①計算出最終的MeasureSpec;
②調用自身的onMeasure()方法,將MeasureSpec傳遞過去;
onMeasure(int widthMeasureSpec, int heightMeasureSpec){}
1、該方法由每個控件自己重寫,因爲不同控件測量規則不一樣;
2、該方法做2件事:
①調用measureChildren()方法測量子控件;
②計算自身尺寸值(子控件測量完畢後,才能確定自身尺寸);
measureChildren(){}
1、該方法在ViewGroup類中,所有ViewGroup子類都調用這個方法;
2、該方法測量子控件;
測量流程總結:View1.measure()——View1.onMeasure()——View1.measureChiledren()——View2.measure()——View2.onMeasure()——View2.measureChiledren()——View3.measure()——View3.onMeasure()——View3.measureChiledren()
佈局
所謂佈局,就是確定控件的位置,即確定其上下左右座標值
layout(int l, int t, int r, int b){}
1、該方法在View類中,所有子類都是調用這個方法(不同控件,在調這個方法之前會進行一些其它處理);
2、參數是自身的上下左右座標值,由父控計算後件傳遞過來;
3、該方法做2件事:
①設置自身上下左右座標值;
②調用自身的onLayout()方法對子控件進行佈局;
onLayout(boolean changed, int left, int top, int right, int bottom){}
1、該方法由每個具體類自己重寫,因爲不同的控件,佈局規則不一樣;
2、該方法佈局子控件;
佈局流程總結:View1.layout()——View1.onLayout()——View2.layout()——View2.onLayout()——View3.layout()——View3.onLayout()
繪製
draw(Canvas canvas){}
1、該方法在View類中,所有子類都是調用這個方法;
2、參數canvas在所有控件中傳遞,即該佈局中的所有子控件都用同一個canvas;
2、這個方法中啓動繪製流程(下面爲簡化流程):一些初始化計算——繪製背景——onDraw()繪製自身——dispatchDraw()繪製子控件;
onDraw(Canvas canvas){}
1、該方法在View類中是空實現,所有具體控件具體實現;
2、該方法繪製自身;
dispatchDraw(Canvas canvas){}
1、該方法在ViewGroup類中,所有子類都是調用這個方法;
3、該方法繪製子控件;
繪製流程總結:View1.draw()——View1.onDraw()——View1.dispatchDraw()——View2.draw()——View2.onDraw()——View2.dispatchDraw()——View3.draw()——View3.onDraw()——View3.dispatchDraw()
總結
1、這三個流程都是在ViewRoot中啓動,啓動方法分別爲performMeasure()、performLayout()、performDraw();
2、measure()、layout()、draw()是啓動流程,沒有各個控件專屬的東西,所以在基類View中,子類共用代碼;
3、onMeasure()、onLayout()、onDraw()是具體的測量、佈局、繪製工作,各個控件不一樣,所以各個控件自己實現;
4、measureChildren()、dispatchDraw()的作用是:遍歷子控件、調用其measure()/draw()方法。沒有各個控件專屬的東西,所以在父類ViewGroup中,子類共用代碼;
5、佈局是個例外,沒有layoutChildren()方法,因爲每個控件佈局規則不一樣,無法重用代碼,所以“佈局子控件”放在onLayout()方法中實現;