Android View 測量與 MeasureSpec 類

前言

前段時(shí)間寫一個(gè)項(xiàng)目笛坦,在布局中出現(xiàn)了 ScrollView 嵌套 ListView,導(dǎo)致 ListView 只能顯示出第一個(gè) item,在網(wǎng)上查了一下,發(fā)現(xiàn)其中一種解決方案代碼量非常少甚垦,是通過自定義一個(gè) ListView,覆寫其中的 onMeasure() 方法涣雕。代碼如下:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
    MeasureSpec.AT_MOST);
    super.onMeasure(widthMeasureSpec, expandSpec);
}

代碼中最重要的地方就是將 View 的測量模式(mode)修改為 AT_MOST。

View 的測量

Android 中的顯示的按鈕闭翩、文本框等組件挣郭,都是需要系統(tǒng)繪制出來的,而在繪制之前疗韵,系統(tǒng)就需要知道每一個(gè)組件的大小兑障,寬高各是多少像素。而這些操作都是在 onMeasure() 方法中進(jìn)行的蕉汪,onMeasure() 方法可以得到 xml 布局文件中配置的寬高流译,然后可以自定義一些操作,最后調(diào)用 setMeasuredDimension() 方法者疤。

onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法有兩個(gè)參數(shù)福澡,這兩個(gè)參數(shù)都是 int 型的。雖然說是 int 型的參數(shù)驹马,但是每個(gè)參數(shù)都包含 2 個(gè)信息革砸,例如 widthMeasureSpec 這個(gè) int 值的高 2 位為測量的模式除秀,低 30 位為測量的大小,也就是 View 的寬的大小算利。之所以使用 int 類型册踩,而不是定義一個(gè)專門的類,Google 官方的解釋如下:

MeasureSpecs are implemented as ints to reduce object allocation.(減少對象分配)

測量模式有三種:

  • EXACTLY 精確模式
    如果我們將 View 的 layout_width 屬性或 layout_height 屬性設(shè)置為 match_parent 或者具體的值效拭,如 50 dp 等暂吉,這是,測量模式就會(huì)被指定為 EXACTLY缎患。
  • AT_MOST 最大值模式
    當(dāng)我們將 View 的 layout_width 屬性或 layout_height 屬性指定為 wrap_content慕的,測量模式就會(huì)被指定為 AT_MOST,這時(shí) View 大小會(huì)隨著它的子 View 大小的變化而變化较锡。
  • UNSPECIFIED 不指定模式
    一般系統(tǒng)不會(huì)指定該模式业稼,只有在自定義 View 時(shí)才會(huì)使用。

MeasureSpec 類

MeasureSpec 類封裝了一些對 MeasureSpecs(指 onMeasure() 方法參數(shù)蚂蕴,int 類型的那個(gè)值) 的一些操作低散,比如 getMode(int) 從 int 類型的值中取出測量模式(也就是高 2 位),getSize(int) 從 int 類型的值中取出長度值(也就是低 30 位)骡楼,makeMeasureSpec(int size, int mode) 根據(jù) size 和 mode 獲得 MeasureSpecs(int 類型的值)熔号。

demo

寫了一個(gè) demo 來理解一下 View 測量的流程。

先新建一個(gè) MyTextView 繼承自 TextView鸟整,覆寫其中的 onMeasure() 方法引镊。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(
                measureWidth(widthMeasureSpec),
                measureHeight(heightMeasureSpec)
        );
    }

    private int measureHeight(int heightMeasureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(heightMeasureSpec);
        int specSize = MeasureSpec.getSize(heightMeasureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

    private int measureWidth(int widthMeasureSpec) {
        int result = 0;
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        int specSize = MeasureSpec.getSize(widthMeasureSpec);

        if (specMode == MeasureSpec.EXACTLY) {
            result = specSize;
        } else {
            result = 200;
            if (specMode == MeasureSpec.AT_MOST) {
                result = Math.min(result, specSize);
            }
        }
        return result;
    }

然后在布局中添加 3 個(gè) MyTextView。

    <cn.zheteng123.onmeasuretest.MyTextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FF0000"/>

    <cn.zheteng123.onmeasuretest.MyTextView
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:background="#00FF00"/>

    <cn.zheteng123.onmeasuretest.MyTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="#0000FF"
        android:text="12345678998765432112345699999999999999999999"/>

最終的效果是這樣子的篮条。

demo 效果

可以看到第三個(gè) TextView 比較特別弟头,我們設(shè)置了寬度和長度為 wrap_content,但是它的寬度與高度并沒有適應(yīng)內(nèi)容的長度涉茧。這是因?yàn)槲覀冊诟矊?onMeasure() 方法時(shí)赴恨,其中有一段是這樣的:

result = 200;
if (specMode == MeasureSpec.AT_MOST) {
    result = Math.min(result, specSize);
}

當(dāng)設(shè)置布局寬度或高度為 wrap_content 時(shí),測量模式就會(huì)是 AT_MOST伴栓,在代碼中伦连,我們將 TextView 的寬度和高度限制在了 200 以內(nèi),超過 200 仍然設(shè)置 200钳垮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末惑淳,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子饺窿,更是在濱河造成了極大的恐慌歧焦,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件肚医,死亡現(xiàn)場離奇詭異倚舀,居然都是意外死亡叹哭,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評論 3 393
  • 文/潘曉璐 我一進(jìn)店門痕貌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來风罩,“玉大人,你說我怎么就攤上這事舵稠〕” “怎么了?”我有些...
    開封第一講書人閱讀 163,624評論 0 353
  • 文/不壞的土叔 我叫張陵哺徊,是天一觀的道長室琢。 經(jīng)常有香客問我,道長落追,這世上最難降的妖魔是什么盈滴? 我笑而不...
    開封第一講書人閱讀 58,356評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮轿钠,結(jié)果婚禮上巢钓,老公的妹妹穿的比我還像新娘。我一直安慰自己疗垛,他們只是感情好症汹,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贷腕,像睡著了一般背镇。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泽裳,一...
    開封第一講書人閱讀 51,292評論 1 301
  • 那天瞒斩,我揣著相機(jī)與錄音,去河邊找鬼涮总。 笑死济瓢,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的妹卿。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔑鹦,長吁一口氣:“原來是場噩夢啊……” “哼夺克!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起嚎朽,我...
    開封第一講書人閱讀 38,992評論 0 275
  • 序言:老撾萬榮一對情侶失蹤铺纽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后哟忍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體狡门,經(jīng)...
    沈念sama閱讀 45,429評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡陷寝,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了其馏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片凤跑。...
    茶點(diǎn)故事閱讀 39,785評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖叛复,靈堂內(nèi)的尸體忽然破棺而出仔引,到底是詐尸還是另有隱情,我是刑警寧澤褐奥,帶...
    沈念sama閱讀 35,492評論 5 345
  • 正文 年R本政府宣布咖耘,位于F島的核電站,受9級特大地震影響撬码,放射性物質(zhì)發(fā)生泄漏儿倒。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評論 3 328
  • 文/蒙蒙 一呜笑、第九天 我趴在偏房一處隱蔽的房頂上張望夫否。 院中可真熱鬧,春花似錦蹈垢、人聲如沸慷吊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽溉瓶。三九已至,卻和暖如春谤民,著一層夾襖步出監(jiān)牢的瞬間堰酿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評論 1 269
  • 我被黑心中介騙來泰國打工张足, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留触创,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評論 2 370
  • 正文 我出身青樓为牍,卻偏偏與公主長得像哼绑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子碉咆,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評論 2 354

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