自定義View-(1)先搞懂測量的所有細(xì)節(jié)

  • 這里我們要知道一個(gè)View的繪制流程中重要的幾個(gè)方法云稚,如果你實(shí)現(xiàn)過嵌套Drawlayout 或者 ScrollView嵌套ListView 就應(yīng)該了解過重繪和測量,篇幅過大我這里就不多贅述了进统,可以參考張興業(yè)博客
    三個(gè)方法的日志打印順序?yàn)?- onMeasure → onLayout → onDraw
    本篇主要記錄我對onMeasure()探究學(xué)習(xí)記錄助币,整個(gè)過程稍微有些反復(fù)但是理解起來會(huì)非常清晰。

這里我們使用源碼比較簡單的TextView來解析螟碎,創(chuàng)建一個(gè)CloneTextView 繼承 TextView眉菱,給出必須構(gòu)造并且重寫 onMeasure()和onDraw()方法。
直接從super.onMeasure()我們進(jìn)入TextView里查看
會(huì)發(fā)現(xiàn)這個(gè)測量規(guī)則類 MeasureSpec
我們來看看他都有什么

//繪制這里僅從源碼中知道有三種模式
//TextView中的源碼    
public static class MeasureSpec {
        
private static final int MODE_SHIFT = 30;
              
//為了秒懂后面的運(yùn)算結(jié)果掉分,這里將2進(jìn)制碼都寫出來
              
//MODE_MASK 換算后 2進(jìn)制為 11000000000000000000000000000000 
private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
              
//換算后 2進(jìn)制為  01000000000000000000000000000000              
public static final int EXACTLY     = 1 << MODE_SHIFT;// int 值 1073741824
             
 //換算后 2進(jìn)制為  10000000000000000000000000000000              
public static final int AT_MOST     = 2 << MODE_SHIFT;//int 值-2147483648 
             
//目前我還沒看到這個(gè)模式有什么實(shí)際卵用俭缓,希望有實(shí)例的同學(xué)能能夠@我              
public static final int UNSPECIFIED = 0 << MODE_SHIFT;//int 值 0
    
//下面我們看看他怎么計(jì)算大小的    
public static int getSize(int measureSpec) {
return (measureSpec & ~MODE_MASK);
    }
    //高2位被忽略,這樣其實(shí)就是measureSpec的大小
    //再看看如何計(jì)算模式的
     public static int getMode(int measureSpec) {
       return (measureSpec & MODE_MASK);
    }
    //因?yàn)楹驧ODE_MASK30位全是0 ,交集是高2位
    //說明父類傳入的spec高2位為測量模式叉抡,后30位為大小尔崔,搞懂了上面的計(jì)算方式,下面onMeasure()的獲取模式和獲取大小就非常容易懂了

我一番百度和翻譯注釋褥民,暫時(shí)是這樣理解的

  • 模式一 強(qiáng)制模式 EXACTLY - 父類給出實(shí)際大小季春,并且作為默認(rèn)值直接使用
  • 模式二 半開放模式 AT_MOST- 父類給出最大值限定,沒有默認(rèn)值,子類自己計(jì)算大小但是不能超過父類限定
  • 模式三 完全開放 UNSPECIFIED 大概意思就是沒有限制消返,你想多大就多大 金箍棒模式
    下面我看看在TextView的onMeasure方法中他是如何處理的
 @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);
  
if (widthMode == MeasureSpec.EXACTLY) {
        // Parent has told us how big to be. So be it.
        width = widthSize;
    } else {
... 
        // Check against our minimum width
        width = Math.max(width, getSuggestedMinimumWidth());
        if (widthMode == MeasureSpec.AT_MOST) { 
           width = Math.min(widthSize, width);
        }
    }
  }

上面的代碼可以直觀的解釋為:

  • 當(dāng)模式為EXACTLY载弄,直接將父類傳入的大小賦值給自己使用
  • 否則如果是AT_MOST模式,則以父類給的大小為上限參考取小的撵颊,也就是最大不能超過父類

上面只是我們從源碼找到的一些頭緒宇攻,實(shí)際場景還未驗(yàn)證,我們的屬性基本都是在xml中設(shè)置的倡勇,下面我們就來驗(yàn)證從xml屬性的設(shè)置到獲取實(shí)際的SpecSize 到計(jì)算出測量模式和實(shí)際的Size逞刷。
我們將源碼中 mode的計(jì)算 和 size的計(jì)算拷貝出來,然后使用一樣的運(yùn)算符來計(jì)算 最后對比一下就知道了方法是笨了點(diǎn)妻熊,但是理解吃透才是最重要的夸浅。

下面我們利用日志打印,直觀的打印出前兩種模式扔役,和最后我們計(jì)算出的實(shí)際模式:

  private int widSpec;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    widSpec = widthMeasureSpec;
    L.printD("CT", "onMeasure");
}
//這里很簡單了帆喇,就是繪制,大小已在onMeasure確定亿胸, 位置已經(jīng)在onLayout 固定
@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    L.printD("CT", "onDraw");
    int MODE_SHIFT = 30;
    int MODE_MASK = 0x3 << MODE_SHIFT;
    int UNSPECIFIED = 0 << MODE_SHIFT;
    int EXACTLY = 1 << MODE_SHIFT;
    int AT_MOST = 2 << MODE_SHIFT;
   L.printD("CT", "AT_MOST=" + AT_MOST);
    L.printD("CT", "EXACTLY=" + EXACTLY); 
   int mode = widSpec & MODE_MASK; 
   L.printD("Ct", "mode==" + mode);
    int size = widSpec & ~MODE_MASK;
    L.printD("Ct", "size==" + size);
}

用上面的代碼一共驗(yàn)證三個(gè)場景

  1. match_parent
  2. wrap_content
  3. 200dip

下面來分析日志信息:
1. match_parent
log-
AT_MOST=-2147483648
EXACTLY=1073741824
mode==1073741824
size==720
模式為 EXACTLY 父類指定大小

2. wrap_content
log-
AT_MOST=-2147483648
EXACTLY=1073741824
mode==-2147483648
size==720
模式為 AT_MOST

  • 這里會(huì)有疑問咯坯钦,specSize還是720 那為什么顯示的大小僅僅是包裹文本內(nèi)容呢,細(xì)心的同學(xué)會(huì)發(fā)現(xiàn)上面的代碼已經(jīng)給出了答案 在TextView的onMeasure方法中预皇,當(dāng)模式為AT_MOST時(shí)他是這樣處理的
  // Check against our minimum width
        width = Math.max(width, getSuggestedMinimumWidth()); 
       if (widthMode == MeasureSpec.AT_MOST) { 
           width = Math.min(widthSize, width);
        } 

這里的widthSize 是父類傳下來的值,再計(jì)算出文本包裹需要的大小婉刀,兩者取最小,一般都是文本包裹值小吟温,所以顯示出來是剛好包裹文本。
200dip
log-
AT_MOST=-2147483648
EXACTLY=1073741824
mode==1073741824
size==400
模式為 EXACTLY 大小

  • 根據(jù)實(shí)際驗(yàn)證我們可以得到如下結(jié)論
    xml-attribute value = match_parent → MODE = EXCATLY
    子類期望與父類一樣大小路星,父類傳入允許子類使用的最大值溯街,并且默認(rèn)使用這個(gè)值
    xml-attribute value = wrap_content → MODE = AT_MOST
    父類傳入允許的最大值诱桂,子類需要為自己重新計(jì)算大小并且參考父類給出的上限
    xml-attribute value = 200dp → MODE = EXCATLY
    父類測量出子類的參數(shù)洋丐,再傳給子類直接使用
    ps:當(dāng)然了這二種模式我們都可以再onMeasure方法中取動(dòng)態(tài)更改繪制模式和大小

上面未驗(yàn)證View(頁面最小單元) MeasureSpec.UNSPECIFIED。因?yàn)檫@里驗(yàn)證沒有使用ViewGroup挥等。也就疏忽了這個(gè)屬性
感謝簡書取名好難的指正

MeasureSpec.UNSPECIFIED 是由特殊父布局測量模式?jīng)Q定的友绝。
通俗講,父布局跟子View同時(shí)使用 MATCH_PARENT 或 WRAP_CONTENT時(shí)
則為MeasureSpec.UNSPECIFIED

邏輯在ViewGroup子類 例如ListView

注釋:根據(jù)父布局測量模式確認(rèn)子View測量模式
onMeasure()>measureScrapChild()>ViewGroup.getChildMeasureSpec()


     public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
···
  // Parent asked to see how big we want to be
        case MeasureSpec.UNSPECIFIED:
            if (childDimension >= 0) {
                // Child wants a specific size... let him have it
                resultSize = childDimension;
                resultMode = MeasureSpec.EXACTLY;
            } else if (childDimension == LayoutParams.MATCH_PARENT) {
                // Child wants to be our size... find out how big it should
                // be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
                // Child wants to determine its own size.... find out how
                // big it should be
                resultSize = View.sUseZeroUnspecifiedMeasureSpec ? 0 : size;
                resultMode = MeasureSpec.UNSPECIFIED;
            }
            break;
···
}

END

參考:
http://tryenough.com/android-MeasureSpec

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末肝劲,一起剝皮案震驚了整個(gè)濱河市迁客,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌辞槐,老刑警劉巖掷漱,帶你破解...
    沈念sama閱讀 218,204評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異榄檬,居然都是意外死亡卜范,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,091評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門鹿榜,熙熙樓的掌柜王于貴愁眉苦臉地迎上來海雪,“玉大人,你說我怎么就攤上這事舱殿“侣悖” “怎么了?”我有些...
    開封第一講書人閱讀 164,548評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵沪袭,是天一觀的道長湾宙。 經(jīng)常有香客問我,道長冈绊,這世上最難降的妖魔是什么侠鳄? 我笑而不...
    開封第一講書人閱讀 58,657評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮焚碌,結(jié)果婚禮上畦攘,老公的妹妹穿的比我還像新娘。我一直安慰自己十电,他們只是感情好知押,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,689評(píng)論 6 392
  • 文/花漫 我一把揭開白布叹螟。 她就那樣靜靜地躺著,像睡著了一般台盯。 火紅的嫁衣襯著肌膚如雪罢绽。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,554評(píng)論 1 305
  • 那天静盅,我揣著相機(jī)與錄音良价,去河邊找鬼。 笑死蒿叠,一個(gè)胖子當(dāng)著我的面吹牛明垢,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播市咽,決...
    沈念sama閱讀 40,302評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼痊银,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了施绎?” 一聲冷哼從身側(cè)響起溯革,我...
    開封第一講書人閱讀 39,216評(píng)論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎谷醉,沒想到半個(gè)月后致稀,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,661評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡俱尼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,851評(píng)論 3 336
  • 正文 我和宋清朗相戀三年抖单,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片号显。...
    茶點(diǎn)故事閱讀 39,977評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡臭猜,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出押蚤,到底是詐尸還是另有隱情蔑歌,我是刑警寧澤,帶...
    沈念sama閱讀 35,697評(píng)論 5 347
  • 正文 年R本政府宣布揽碘,位于F島的核電站次屠,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏雳刺。R本人自食惡果不足惜劫灶,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,306評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望掖桦。 院中可真熱鬧本昏,春花似錦、人聲如沸枪汪。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,898評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宿稀,卻和暖如春趁舀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背祝沸。 一陣腳步聲響...
    開封第一講書人閱讀 33,019評(píng)論 1 270
  • 我被黑心中介騙來泰國打工矮烹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罩锐。 一個(gè)月前我還...
    沈念sama閱讀 48,138評(píng)論 3 370
  • 正文 我出身青樓奉狈,卻偏偏與公主長得像,于是被迫代替她去往敵國和親唯欣。 傳聞我的和親對象是個(gè)殘疾皇子嘹吨,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,927評(píng)論 2 355

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