簡單梳理一下View的繪製流程。View的繪製分爲三個部分(mechanism),分別爲:measure,layout,draw。
第一部分:Measure
這一過程負責確定各個View
的大小,得到的結果存放在每個View
的mMeasuredWidth
,mMeasuredHeight
中。
該部分主要有三個函數:
measure(int widthMeasureSpec, int heightMeasureSpec);
onMeasure(int widthMeasureSpec, int heightMeasureSpec);
setMeasuredDimension(int measuredWidth, int measuredHeight);
measure(int, int)
發起measure過程,需要用到兩類信息:
MeasureSpecs
,父View
對子View
的要求,有三種模式:UNSPECIFIED
,EXACTLY
,AT_MOST
ViewGroup.LayoutParams
,子View
自身的佈局參數,可以是具體值、MATCH_PARENT
或WRAP_CONTENT
。
該方法爲final
方法,不可被Override
,該方法會調用onMeasure()
,一般情況下子類應該Override
onMeasure()
方法。
onMeasure(int, int)
進行具體的測量工作,該函數的參數是mMeasuredWidth
和mMeasuredHeight
,即父View
對我們的測量要求,要做的工作就是根據父View
的大小確定自己的大小。如果是ViewGroup
,還要調用子View
的measure(int, int)
才能確定自己的大小。同時這一步的最後需要調用setMeasuredDimension(int, int)
將測量結果存儲起來,否則在使用過程中會拋出異常。
setMeasuredDimension(int, int)
將測量結果存儲在mMeasuredWidth
,mMeasuredHeight
中,在之後的步驟中,可以通過getMeasuredWidth()
和getMeasuredHeight()
獲取這兩個參數。
第二部分:Layout
這一部分通過Measure中得到的各個View
的大小,確定各個View
的擺放位置。
該部分主要有兩個函數:
layout(int l, int t, int r, int b);
onLayout(boolean changed, int l, int t, int r, int b);
相對於measure,這部分比較簡單,最後會通過setFrame(int, int, int, int)確定各個View相對於父View的座標。
第三部分:Draw
該部分繪製View的內容。
主要有兩個函數:
draw(Canvas canvas);
onDraw(Canva canvas);
draw(Canvas)
代碼略長,但主要工作是調用onDraw()
繪製自己,然後調用dispatchDraw()
繪製子View
。
根據文檔說明,該方法負責渲染將View以及所有子View渲染到Canvas,必須在layout完成後調用,一般不應該Override該方法,如果要繼承,可以Override onDraw()方法。
也就是說自定義View的時候基本可以不考慮這個方法。
onDraw(Canvas)
View
的onDraw()
方法爲空實現,subclass一般要Override
該方法,繪製自己。
比如,想繪製一個矩形:
// 畫一個起點座標(0,0),終點座標(10,20)的矩形
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.BLUE);
paint.setStyle(Paint.Style.FILL);
canvas.drawRect(0, 0, 10, 20, paint);
dispatchDraw(Canvas)
調用子View
的draw()
方法,繪製子View
。