View的繪制流程

認識一個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悉尾。

Activity界面結(jié)構(gòu).png

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的繪制流程

View的繪制流程.png

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ī)律如下:


childMeasureSpec的形成.png

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

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蓉坎,一起剝皮案震驚了整個濱河市澳眷,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蛉艾,老刑警劉巖钳踊,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勿侯,居然都是意外死亡拓瞪,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進店門助琐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吴藻,“玉大人,你說我怎么就攤上這事弓柱」当ぃ” “怎么了侧但?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長航罗。 經(jīng)常有香客問我禀横,道長,這世上最難降的妖魔是什么粥血? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任柏锄,我火速辦了婚禮,結(jié)果婚禮上复亏,老公的妹妹穿的比我還像新娘趾娃。我一直安慰自己,他們只是感情好缔御,可當我...
    茶點故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布抬闷。 她就那樣靜靜地躺著,像睡著了一般耕突。 火紅的嫁衣襯著肌膚如雪笤成。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天眷茁,我揣著相機與錄音炕泳,去河邊找鬼。 笑死上祈,一個胖子當著我的面吹牛培遵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播登刺,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼籽腕,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了塘砸?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤晤锥,失蹤者是張志新(化名)和其女友劉穎掉蔬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矾瘾,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡女轿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了壕翩。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蛉迹。...
    茶點故事閱讀 39,727評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖放妈,靈堂內(nèi)的尸體忽然破棺而出北救,到底是詐尸還是另有隱情荐操,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布珍策,位于F島的核電站托启,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏攘宙。R本人自食惡果不足惜屯耸,卻給世界環(huán)境...
    茶點故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望蹭劈。 院中可真熱鬧疗绣,春花似錦、人聲如沸铺韧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽祟蚀。三九已至工窍,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間前酿,已是汗流浹背患雏。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留罢维,地道東北人淹仑。 一個月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像肺孵,于是被迫代替她去往敵國和親匀借。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,619評論 2 354

推薦閱讀更多精彩內(nèi)容