Android 中 Activity 是作為應(yīng)用程序的載體存在耿芹,代表著一個(gè)完整的用戶(hù)界面猩系,提供了一個(gè)窗口來(lái)繪制各種視圖中燥,當(dāng) Activity 啟動(dòng)時(shí),我們會(huì)通過(guò) setContentView 方法來(lái)設(shè)置一個(gè)內(nèi)容視圖吟秩,這個(gè)內(nèi)容視圖就是用戶(hù)看到的界面涵防。
PhoneWindow 是 Android 系統(tǒng)中最基本的窗口系統(tǒng)壮池,每個(gè) Activity 會(huì)創(chuàng)建一個(gè)椰憋。PhoneWindow 是 Activity 和 View 系統(tǒng)交互的借口赔退。DecorView 本質(zhì)上是一個(gè) FrameLayout硕旗,是 Activity 中所有 View 的祖先漆枚。
繪制的整體流程
當(dāng)一個(gè)應(yīng)用啟動(dòng)時(shí)浪读,會(huì)啟動(dòng)一個(gè)主 Activity,Android 系統(tǒng)會(huì)根據(jù) Activity 的布局來(lái)對(duì)它進(jìn)行繪制互订。繪制會(huì)從根視圖 ViewRoot 的 performTraversals() 方法開(kāi)始仰禽,從上到下遍歷整個(gè)視圖樹(shù)吐葵,每個(gè) View 控制負(fù)責(zé)繪制自己温峭,而 ViewGroup 還需要負(fù)責(zé)通知自己的子 View 進(jìn)行繪制操作。視圖操作的過(guò)程可以分為三個(gè)步驟凤藏,分別是測(cè)量(Measure)、布局(Layout)和繪制(Draw)栗菜。performTraversals 方法在類(lèi) ViewRootImpl 內(nèi)疙筹,其核心代碼如下而咆。
int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
...
// 測(cè)量
performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
...
// 布局
performLayout(lp, mWidth, mHeight);
...
// 繪制
performDraw();
MeasureSpec
MeasureSpec 表示的是一個(gè) 32 位的整數(shù)值翘盖,它的高 2 位表示測(cè)量模式 SpecMode馍驯,低 30 位表示某種測(cè)量模式下的規(guī)格大小 SpecSize汰瘫。MeasureSpec 是 View 類(lèi)的一個(gè)靜態(tài)內(nèi)部類(lèi)混弥,用來(lái)說(shuō)明應(yīng)該如何測(cè)量這個(gè)View蝗拿。
三種測(cè)量模式蒿涎。
- UNSPECIFIED:不指定測(cè)量模式仓手,父視圖沒(méi)有限制子視圖的大小玻淑,子視圖可以是想要的任何尺寸补履,通常用于系統(tǒng)內(nèi)部箫锤,應(yīng)用開(kāi)發(fā)中很少使用到驰弄。
- EXACTLY:精確測(cè)量模式,當(dāng)該視圖的 layout_width 或者 layout_height 指定為具體數(shù)值或者 match_parent 時(shí)生效速客,表示父視圖已經(jīng)決定了子視圖的精確大小,這種模式下 View 的測(cè)量值就是 SpecSize 的值五鲫。
- AT_MOST:最大值模式溺职,當(dāng)前視圖的 layout_width 或者 layout_height 指定為 wrap_content 時(shí)生效,此時(shí)子視圖的尺寸可以是不超過(guò)父視圖運(yùn)行的最大尺寸的任何尺寸位喂。
對(duì) DecorView 而言浪耘,它的 MeasureSpec 由窗口尺寸和其自身的 LayoutParams 共同決定;對(duì)于普通的 View塑崖,它的 MeasureSpec 由父視圖的 MeasureSpec 和其本身的 LayoutParams 共同決定七冲。
Measure
Measure 用來(lái)計(jì)算 View 的實(shí)際大小。頁(yè)面的測(cè)量流程從 performMeasure 方法開(kāi)始规婆。
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
}
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "measure");
try {
mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
} finally {
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
具體操作是分發(fā)給 ViewGroup 的澜躺,由 ViewGroup 在它的 measureChild 方法中傳遞給子 View掘鄙。ViewGroup 通過(guò)遍歷自身所有的子 View饿这,并逐個(gè)調(diào)用子 View 的 measure 方法實(shí)現(xiàn)測(cè)量操作。
// 遍歷測(cè)量 ViewGroup 中所有的 View
protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
final int size = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < size; ++i) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
}
}
}
// 測(cè)量某個(gè)指定的 View
protected void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
final LayoutParams lp = child.getLayoutParams();
final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
mPaddingLeft + mPaddingRight, lp.width);
final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
mPaddingTop + mPaddingBottom, lp.height);
child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
View (ViewGroup) 的 Measure 方法赵抢,最終的測(cè)量是通過(guò)回調(diào) onMeasure 方法實(shí)現(xiàn)的,這個(gè)通常由 View 的特定子類(lèi)自己實(shí)現(xiàn)冒冬,可以通過(guò)重寫(xiě)這個(gè)方法實(shí)現(xiàn)自定義 View摇幻。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
...
onMeasure(widthMeasureSpec, heightMeasureSpec);
....
}
// 如果需要自定義測(cè)量,子類(lèi)需重寫(xiě)這個(gè)方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
// 如果 View 沒(méi)有重寫(xiě)onMeasure 方法帜矾,默認(rèn)會(huì)直接調(diào)用 getDefaultSize
public static int getDefaultSize(int size, int measureSpec) {
int result = size;
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
result = size;
break;
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
return result;
}
Layout
Layout 過(guò)程用來(lái)確定 View 在父容器的布局位置灭衷,他是父容器獲取子 View 的位置參數(shù)后,調(diào)用子 View 的 layout 方法并將位置參數(shù)傳入實(shí)現(xiàn)的。ViewRootImpl 的 performLayout 代碼如下由缆。
private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,
int desiredWindowHeight) {
...
host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());
...
}
View 的 layout 方法代碼舔箭。
public void layout(int l, int t, int r, int b) {
onLayout(changed, l, t, r, b);
}
// 空方法箫章,子類(lèi)如果是 ViewGroup 類(lèi)型桶至,則重寫(xiě)這個(gè)方法,實(shí)現(xiàn) ViewGroup 中所有 View 控件布局
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
}
Draw
Draw 操作用來(lái)將控件繪制出來(lái)飒泻,繪制的流程從 performDraw 方法開(kāi)始史辙。performDraw 方法在類(lèi) ViewRootImpl 內(nèi)耙蔑,其核心代碼如下。
private void performDraw() {
boolean canUseAsync = draw(fullRedrawNeeded);
}
private boolean draw(boolean fullRedrawNeeded) {
...
if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,
scalingRequired, dirty, surfaceInsets)) {
return false;
}
}
private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,
boolean scalingRequired, Rect dirty, Rect surfaceInsets) {
...
mView.draw(canvas);
...
}
最終調(diào)用到每個(gè) View 的 draw 方法繪制每個(gè)具體的 View寥院,繪制基本上可以分為六個(gè)步驟塑煎。
public void draw(Canvas canvas) {
...
// Step 1, draw the background, if needed
if (!dirtyOpaque) {
drawBackground(canvas);
}
...
// Step 2, save the canvas' layers
saveCount = canvas.getSaveCount();
...
// Step 3, draw the content
if (!dirtyOpaque) onDraw(canvas);
// Step 4, draw the children
dispatchDraw(canvas);
// Step 5, draw the fade effect and restore layers
canvas.drawRect(left, top, right, top + length, p);
...
canvas.restoreToCount(saveCount);
...
// Step 6, draw decorations (foreground, scrollbars)
onDrawForeground(canvas);
}