UI繪制流程(三)

UI繪制流程的起始點(diǎn) ViewRootImpl#performTraversals()方法中:

此方法里分別調(diào)用了:
///測(cè)量
performMeasure()
// 擺放布局
performLayout()
// 繪制
performDraw()

這也是我們自定義UI布局時(shí)注意的過(guò)程 : 測(cè)量(Measure)—>布局(Layout)—>繪制(Draw)


Measure測(cè)量過(guò)程:

1. 通過(guò)getRootMeasureSpec(int windowSize, int rootDimension)方法傳入父容器windowSize(具體數(shù)值) rootDimension (LayoutParams.(width|height))獲取子view寬高測(cè)量模式;
具體代碼:
 private static int getRootMeasureSpec(int windowSize, int rootDimension) {
        int measureSpec;
        switch (rootDimension) {

        case ViewGroup.LayoutParams.MATCH_PARENT:
            // 窗口不能調(diào)整大小。強(qiáng)制根視圖為windowSize
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
            break;
        case ViewGroup.LayoutParams.WRAP_CONTENT:
            // 窗口可以調(diào)整大小入偷。設(shè)置根視圖的最大大小。
            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
            break;
        default:
            // 窗口想成為一個(gè)確切的大小廊宪。強(qiáng)制根視圖是那個(gè)大小。
            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
            break;
        }
        return measureSpec;
    }
MeasureSpec:

在Measure流程中,系統(tǒng)將View的LayoutParams根據(jù)父容器所施加的規(guī)則轉(zhuǎn)換成對(duì)應(yīng)的MeasureSpec,在onMeasure中根據(jù)這個(gè)MeasureSpec來(lái)確定view的測(cè)量寬高

測(cè)量模式:
  • EXACTLY :父容器已經(jīng)測(cè)量出所需要的精確大小阳懂,這也是childview的最終大小------match_parent,精確值
  • ATMOST : child view最終的大小不能超過(guò)父容器的給的------wrap_content
  • UNSPECIFIED: 不確定柜思,源碼內(nèi)部使用-------一般在ScrollView,ListView
MeasureSpec里通過(guò)和一個(gè)數(shù)值M size 模式 與或非運(yùn)算 得到一個(gè)數(shù)值(測(cè)量模式值) 而后通過(guò)M進(jìn)行一些運(yùn)算可以拿到父容器 size 模式
2. 調(diào)用performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) 兩個(gè)參數(shù)是根據(jù)父容器的寬高測(cè)量模式 巷燥,在方法中 調(diào)用 mView.measure(childWidthMeasureSpec, childHeightMeasureSpec)又在這個(gè)方法里調(diào)用了onMeasure(int widthMeasureSpec, int heightMeasureSpec) 而 mView 為DecorView 而赡盘,DecorView繼承FrameLayout ,onMeasure方法在FrameLayout被重寫了缰揪,所以最終調(diào)用的是FrameLayout onMeasure方法陨享;
3. FrameLayout onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法:
容器view

1.獲取子view數(shù)并遍歷;
2.遍歷過(guò)程 獲取view child 判斷 child.getVisibility() != GONE 時(shí)調(diào)用measureChildWithMargins()方法測(cè)量子view
遍歷代碼:

 for (int i = 0; i < count; i++) 

            final View child = getChildAt(i);//獲取子view

            if (mMeasureAllChildren || child.getVisibility() != GONE) {//判斷GONE 是GONE不用測(cè)量

                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);//測(cè)量child以及它的子view

                final LayoutParams lp = (LayoutParams) child.getLayoutParams();//獲取view LayoutParams 
                //獲取所有子view中最大的寬或高
                maxWidth = Math.max(maxWidth,child.getMeasuredWidth() + lp.leftMargin + lp.rightMargin);
                maxHeight = Math.max(maxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
                childState = combineMeasuredStates(childState, child.getMeasuredState());
                if (measureMatchParentChildren) {
                    if (lp.width == LayoutParams.MATCH_PARENT || lp.height == LayoutParams.MATCH_PARENT) {
                        mMatchParentChildren.add(child);
                    }
                }
            }
        }

2.1.measureChildWithMargins()方法中傳入了 child:子view钝腺, parentWidthMeasureSpec:父容器寬模式 parentHeightMeasureSpec:父容器高模式 等
2.2.measureChildWithMargins()具體執(zhí)行為 代碼:

    protected void measureChildWithMargins(View child,  int parentWidthMeasureSpec, int widthUsed,  int parentHeightMeasureSpec, int heightUsed) {
        //獲取子view MarginLayoutParams
        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
       //根據(jù)子view寬或高對(duì)應(yīng)的  父容器模式  Padding Margin width 獲取該控件的測(cè)量模式
        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
                        + widthUsed, lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
                        + heightUsed, lp.height);
         //繼續(xù)測(cè)量子view 的子view 直到視圖view為止
        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
    }
  1. 遍歷完成之后獲取了子view中最大寬 或 高 調(diào)用setMeasuredDimension()方法為該view設(shè)置寬高
setMeasuredDimension()后才可以獲得view的寬高

小結(jié):ViewGroup遍歷測(cè)量Child三方法 自定義中使用:

  • measureChildWithMargins // 有Margin測(cè)量
  • measureChild// 測(cè)量這個(gè)view 沒(méi)有Margin測(cè)量 自己遍歷
  • measureChildren//遍歷所有子view完成沒(méi)有Margin測(cè)量
視圖view

根據(jù)view設(shè)置內(nèi)容 或呈現(xiàn)內(nèi)容 來(lái)完成測(cè)量
setMeasuredDimension()調(diào)用完成測(cè)量

測(cè)量總結(jié) 自定義View抛姑,ViewGroup:

View:
  • 套路:根據(jù)父容器傳來(lái)的測(cè)量模式確定view寬高 以及自己內(nèi)容 最終調(diào)用setMeasuredDimession方法來(lái)保存自己的測(cè)量寬高
            final int specMode = MeasureSpec.getMode(measureSpec);
            final int specSize =  MeasureSpec.getSize(measureSpec);
            switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
             
                break;
            case MeasureSpec.AT_MOST:
                
                break;
            case MeasureSpec.EXACTLY:
            //完成寬高測(cè)量
                break;
        }
       setMeasuredDimension(width, height);
ViewGroup
  • 1、測(cè)量子view的規(guī)格大小 measureChildWithMargins measureChild measureChildren等方法
  • 2艳狐、通過(guò)子view的規(guī)格大小來(lái)確定自己的大小 setMeasuredDimession

Layout測(cè)量過(guò)程:

大概過(guò)程是Measure一樣

ViewGroup

重寫onLayout() 根據(jù)里要的樣式計(jì)算每個(gè)view left, top, right, bottom 在調(diào)用子view layout(left, top, right, bottom)方法完成布局

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末定硝,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子毫目,更是在濱河造成了極大的恐慌蔬啡,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件镀虐,死亡現(xiàn)場(chǎng)離奇詭異箱蟆,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)刮便,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門空猜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事辈毯“犹郏” “怎么了?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵漓摩,是天一觀的道長(zhǎng)裙士。 經(jīng)常有香客問(wèn)我,道長(zhǎng)管毙,這世上最難降的妖魔是什么腿椎? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮夭咬,結(jié)果婚禮上啃炸,老公的妹妹穿的比我還像新娘。我一直安慰自己卓舵,他們只是感情好南用,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掏湾,像睡著了一般裹虫。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上融击,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天筑公,我揣著相機(jī)與錄音,去河邊找鬼尊浪。 笑死匣屡,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的拇涤。 我是一名探鬼主播捣作,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼鹅士!你這毒婦竟也來(lái)了券躁?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤如绸,失蹤者是張志新(化名)和其女友劉穎嘱朽,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體怔接,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡搪泳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了扼脐。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片岸军。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡奋刽,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出艰赞,到底是詐尸還是另有隱情佣谐,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布方妖,位于F島的核電站狭魂,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏党觅。R本人自食惡果不足惜雌澄,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杯瞻。 院中可真熱鬧镐牺,春花似錦、人聲如沸魁莉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)旗唁。三九已至畦浓,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間检疫,已是汗流浹背宅粥。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留电谣,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓抹蚀,卻偏偏與公主長(zhǎng)得像剿牺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子环壤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

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