組件測(cè)量那些事

簡(jiǎn)介

組件在頁面上的展示要經(jīng)過測(cè)量、布局晴股、繪制三個(gè)過程城须,而測(cè)量就是把我們定義的組件寬和高轉(zhuǎn)換為具體的大小的這個(gè)過程。
我們常見的組件的寬高定義如下:

<TextView
    android:id="@+id/name1"
    android:layout_width="100dp"
    android:layout_height="100dp" />
<TextView
    android:id="@+id/name1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content" />
<TextView
    android:id="@+id/name1"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

寬高定義為100dp凹蜈,那么最后組件的寬高就是100dp限寞,這很好理解,那wrap_content和match_parent呢仰坦,我們都知道履植,wrap_content根據(jù)組件自身的大小變化而變化,match_parent指的要占滿父視圖悄晃。那么系統(tǒng)具體是如何解析的呢玫霎?

系統(tǒng)把組件的長(zhǎng)寬定義轉(zhuǎn)換相應(yīng)的測(cè)量規(guī)則

在頁面測(cè)量的時(shí)候都會(huì)調(diào)用measureChild或measureChildWithMargins方法對(duì)組件進(jìn)行測(cè)量凿滤,已measureChild為例:

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);}

關(guān)于MeasureSpec的定義就不再這里闡述了,大家可以查看其他文章庶近。
這段代碼很短翁脆,主要就是描述了通過getChildMeasureSpec把父視圖的測(cè)量規(guī)則和組件的布局參數(shù)轉(zhuǎn)換為子視圖的測(cè)量規(guī)則,進(jìn)行測(cè)量鼻种。再來看getChildMeasureSpec方法:

public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
    int specMode = MeasureSpec.getMode(spec);
    int specSize = MeasureSpec.getSize(spec);
    int size = Math.max(0, specSize - padding);
    int resultSize = 0;
    int resultMode = 0;
    switch (specMode) {
    // Parent has imposed an exact size on us
    case MeasureSpec.EXACTLY:
        if (childDimension >= 0) {
            resultSize = childDimension;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size. So be it.
            resultSize = size;
            resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us.
            resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // Parent has imposed a maximum size on us
    case MeasureSpec.AT_MOST:
        if (childDimension >= 0) {
            // Child wants a specific size... so be it
            resultSize = childDimension; 
           resultMode = MeasureSpec.EXACTLY;
        } else if (childDimension == LayoutParams.MATCH_PARENT) {
            // Child wants to be our size, but our size is not fixed.
            // Constrain child to not be bigger than us. 
           resultSize = size;
            resultMode = MeasureSpec.AT_MOST; 
       } else if (childDimension == LayoutParams.WRAP_CONTENT) {
            // Child wants to determine its own size. It can't be
            // bigger than us. 
           resultSize = size;
            resultMode = MeasureSpec.AT_MOST;
        }
        break;
    // Parent asked to see how big we want to be
    case MeasureSpec.UNSPECIFIED:
       ... 
   }
   return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}

1反番、如果childDimension >0,指的就是組件定義了具體的大胁嬖俊(MATCH_PARENT為-1罢缸,WRAP_CONTENT為-2),例如:

<TextView
    android:id="@+id/name1"
    android:layout_width="100dp"
    android:layout_height="100dp" />

組件的大小就是childDimension 投队,測(cè)量模式為MeasureSpec.EXACTLY枫疆。
2、如果childDimension == LayoutParams.MATCH_PARENT敷鸦,組件大小就是父視圖提供的大小size,而測(cè)量模式也是MeasureSpec.EXACTLY息楔。
3、如果childDimension == LayoutParams.WRAP_CONTENT扒披,組件的測(cè)量模式為MeasureSpec.AT_MOST值依,大小為父視圖提供的大小size,這里的大小實(shí)際上指的是組件的最大大小,組件的具體大小的測(cè)量還需要組件自己實(shí)現(xiàn)谎碍。
4、如果父視圖的測(cè)量模式為MeasureSpec.AT_MOST洞焙,及時(shí)組件childDimension == LayoutParams.MATCH_PARENT蟆淀,其測(cè)量模式也為MeasureSpec.AT_MOST。

組件如何根據(jù)測(cè)量模式測(cè)量出其大小的

child.measure(childWidthMeasureSpec, childHeightMeasureSpec)方法最終就會(huì)調(diào)用組件的onMeasure方法澡匪,在這里組件會(huì)根據(jù)測(cè)量規(guī)則計(jì)算出具體的寬高大小熔任。

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

getDefaultSize方法就是計(jì)算出具體的寬高,setMeasuredDimension方法則是設(shè)置寬高唁情。
基類View的具體計(jì)算方法是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;
}

無論是MeasureSpec.AT_MOST還是MeasureSpec.EXACTLY模式疑苔,最終得到的具體大小都為父視圖提供大小,這與我們對(duì)wrap_content的理解是有出入的甸鸟。其實(shí)具體的組件都需要重寫onMeasure方法的惦费。
例如TextView:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int widthMode = MeasureSpec.getMode(widthMeasureSpec);
    int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    int widthSize = MeasureSpec.getSize(widthMeasureSpec);
    int heightSize = MeasureSpec.getSize(heightMeasureSpec);
    int width;
    int height;
    if (widthMode == MeasureSpec.EXACTLY) {
        // Parent has told us how big to be. So be it.
        width = widthSize;
    } else {
      ...這里計(jì)算textView的大小width....       
        if (widthMode == MeasureSpec.AT_MOST) {
            width = Math.min(widthSize, width);
        }
    }
...
setMeasuredDimension(width, height);
}

1、如果模式為MeasureSpec.EXACTLY抢韭,大小就是父視圖提供的大小
2薪贫、如果模式是MeasureSpec.AT_MOST,會(huì)取textView的真實(shí)大小與與父視圖提供的大小中小的那個(gè)刻恭,這樣與我們理解的wrap_content就吻合了瞧省。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子鞍匾,更是在濱河造成了極大的恐慌交洗,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,042評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件橡淑,死亡現(xiàn)場(chǎng)離奇詭異构拳,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)梳码,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,996評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門隐圾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人掰茶,你說我怎么就攤上這事暇藏。” “怎么了濒蒋?”我有些...
    開封第一講書人閱讀 156,674評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵盐碱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我沪伙,道長(zhǎng)瓮顽,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,340評(píng)論 1 283
  • 正文 為了忘掉前任围橡,我火速辦了婚禮暖混,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘翁授。我一直安慰自己拣播,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,404評(píng)論 5 384
  • 文/花漫 我一把揭開白布收擦。 她就那樣靜靜地躺著贮配,像睡著了一般。 火紅的嫁衣襯著肌膚如雪塞赂。 梳的紋絲不亂的頭發(fā)上泪勒,一...
    開封第一講書人閱讀 49,749評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音宴猾,去河邊找鬼圆存。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仇哆,可吹牛的內(nèi)容都是我干的辽剧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,902評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼税产,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼怕轿!你這毒婦竟也來了偷崩?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,662評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤撞羽,失蹤者是張志新(化名)和其女友劉穎阐斜,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诀紊,經(jīng)...
    沈念sama閱讀 44,110評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡谒出,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,451評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邻奠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笤喳。...
    茶點(diǎn)故事閱讀 38,577評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖碌宴,靈堂內(nèi)的尸體忽然破棺而出杀狡,到底是詐尸還是另有隱情,我是刑警寧澤贰镣,帶...
    沈念sama閱讀 34,258評(píng)論 4 328
  • 正文 年R本政府宣布呜象,位于F島的核電站,受9級(jí)特大地震影響碑隆,放射性物質(zhì)發(fā)生泄漏恭陡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,848評(píng)論 3 312
  • 文/蒙蒙 一上煤、第九天 我趴在偏房一處隱蔽的房頂上張望休玩。 院中可真熱鬧,春花似錦劫狠、人聲如沸拴疤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,726評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遥赚。三九已至扬舒,卻和暖如春阐肤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背讲坎。 一陣腳步聲響...
    開封第一講書人閱讀 31,952評(píng)論 1 264
  • 我被黑心中介騙來泰國(guó)打工孕惜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人晨炕。 一個(gè)月前我還...
    沈念sama閱讀 46,271評(píng)論 2 360
  • 正文 我出身青樓衫画,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親瓮栗。 傳聞我的和親對(duì)象是個(gè)殘疾皇子削罩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,452評(píng)論 2 348

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