認識一個Activity界面結(jié)構(gòu)
每個Activity都會有一個Window揽碘,Window是抽象類具體由PhoneWindow實現(xiàn)单鹿。PhoneWindow中有一個頂級View即DecorView,DecorView是一個FrameLayout天梧,有唯一的子view垂直布局的LinearLayout盔性,包含兩個子元素,一個是TitleView(ActionBar的容器)呢岗,另一個是ContentView(窗口內(nèi)容的容器)冕香。關(guān)于ContentView,它是一個FrameLayout(android.R.id.content)后豫,我們平常用的setContentView就是設(shè)置它的子View悉尾。
RootView
View的測量(measure),布局(layout)挫酿,繪制(draw)的三大流程都由RootView完成构眯,RootViewImpl是RootView的具體實現(xiàn)類。在Activity被創(chuàng)建的完成后早龟,會將DecorView添加到Window中惫霸,同時創(chuàng)建RootViewImpl對象,并將RootViewImpl與DecorView建立關(guān)聯(lián)葱弟。View真正的繪制流程是從performTraversals開始的它褪。
View的繪制流程
Measure測量過程
在measure測量階段:起點是ViewRootImpl的measureHierarchy()方法---->在measureHierarchy中會執(zhí)行到performMeasure(childWidthMeasureSpec, childHeightMeasureSpec),傳入performMeasure()方法的MeasureSpec的SpecMode為EXACTLY,SpecSize為窗口尺寸---->在performMeasure方法中調(diào)用mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)翘悉,mView就是DecorView---->對于decorView來說,實際執(zhí)行測量工作的是FrameLayout的onMeasure()方法居触。在onMeasure中調(diào)用measureChildWithMargins()方法對所有子View進行了一遍測量妖混,并計算出所有子View的最大寬度和最大高度。
對于ViewGroup來說轮洋,它會調(diào)用child.measure()來完成子View的測量制市。傳入ViewGroup的MeasureSpec是它的父View用于約束其測量的,那么ViewGroup本身也需要生成一個childMeasureSpec來限制它的子View的測量工作弊予。這個childMeasureSpec就由getChildMeasureSpec()方法生成祥楣,childMeasureSpec的形成規(guī)律如下:
View的measure過程由measure方法來完成,measure方法是一個final類型的方法汉柒,子類不能重寫此方法误褪。在View的measure方法中會去調(diào)用view的onMeasure方法。
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
setMeasuredDimension會設(shè)置view測量的寬高碾褂。注意這里是測量的寬高兽间,并非最終的寬高,雖然絕大數(shù)情況下測量寬高和最終寬高一樣正塌,但是最終的寬高還是由layout來決定嘀略。getMeasuredWidth()恤溶,getMeasuredHeight()方法用來獲得測量的寬高,通過這兩個方法在onMeasure中不一定能獲得準確的測量寬高帜羊,因為有時候或多次調(diào)用onMeasure咒程,所以在onLayout中去獲得測量寬高是最保險的方式。
Layout布局過程
layout確定View本身的位置讼育,onLayout確定子元素的位置
下面是view中l(wèi)ayout的源碼:先是通過setFrame的方法來確定view自己的位置帐姻,然后再onLayout的方法來確定子view的布局,onLayout沒有統(tǒng)一的實現(xiàn)窥淆,不同的viewGroup由不同的實現(xiàn)卖宠。
public void layout(int l, int t, int r, int b) {
if ((mPrivateFlags3 & PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT) != 0) {
onMeasure(mOldWidthMeasureSpec, mOldHeightMeasureSpec);
mPrivateFlags3 &= ~PFLAG3_MEASURE_NEEDED_BEFORE_LAYOUT;
}
int oldL = mLeft;
int oldT = mTop;
int oldB = mBottom;
int oldR = mRight;
boolean changed = isLayoutModeOptical(mParent) ?
setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
if (changed || (mPrivateFlags & PFLAG_LAYOUT_REQUIRED) == PFLAG_LAYOUT_REQUIRED) {
onLayout(changed, l, t, r, b);
mPrivateFlags &= ~PFLAG_LAYOUT_REQUIRED;
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnLayoutChangeListeners != null) {
ArrayList<OnLayoutChangeListener> listenersCopy =
(ArrayList<OnLayoutChangeListener>)li.mOnLayoutChangeListeners.clone();
int numListeners = listenersCopy.size();
for (int i = 0; i < numListeners; ++i) {
listenersCopy.get(i).onLayoutChange(this, l, t, r, b, oldL, oldT, oldR, oldB);
}
}
}
mPrivateFlags &= ~PFLAG_FORCE_LAYOUT;
mPrivateFlags3 |= PFLAG3_IS_LAID_OUT;
}
getMeasureHeight()和getHeight(),測量寬高和最終寬高的區(qū)別
getMeasureHeight()是在measure之后獲得忧饭,getHeight()是在layout之后獲得的扛伍,兩個絕大多數(shù)情況下是一樣的,只有重寫layout方法之后會不同词裤。在下面這種情況下刺洒,getHeight比getMesaureHeight大100
public void layout(int l, int t, int r, int b) {
super.layout(l, t, r+100, b+100);
}
draw繪制流程
實際上,View類的onDraw()方法為空吼砂,因為每個View繪制自身的方式都不盡相同逆航,對于decorView來說,由于它是容器View渔肩,所以它本身并沒有什么要繪制的因俐。dispatchDraw()方法用于繪制子View,顯然普通View(非ViewGroup)并不能包含子View周偎,所以View類中這個方法的實現(xiàn)為空抹剩。ViewGroup類的dispatchDraw()方法中會依次調(diào)用drawChild()方法來繪制子View。
draw過程簡單遵循如下幾步:
(1)繪制背景backgroud.draw(canvas)
(2)繪制自己(onDraw)
(3) 繪制children(dispatchDraw)
(4)繪制裝飾 onDrawScrollBars