Android自定義View:你知道通過getWidth() 與 getMeasuredWidth() 獲取寬高的區(qū)別嗎?


前言

  • 在自定義View的過程中,使用getMeasuredWidth() / getMeasuredHeight() 與 getWidth() / getHeight()都能獲取View的寬 / 高,但是二者有什么區(qū)別呢蛋铆?
  • 今天,我將深入源碼放接,給大家分析二者之間的區(qū)別刺啦,希望你們會(huì)喜歡。

Carson帶你學(xué)Android自定義View文章系列:
Carson帶你學(xué)Android:自定義View基礎(chǔ)
Carson帶你學(xué)Android:一文梳理自定義View工作流程
Carson帶你學(xué)Android:自定義View繪制準(zhǔn)備-DecorView創(chuàng)建
Carson帶你學(xué)Android:自定義View Measure過程
Carson帶你學(xué)Android:自定義View Layout過程
Carson帶你學(xué)Android:自定義View Draw過程
Carson帶你學(xué)Android:手把手教你寫一個(gè)完整的自定義View
Carson帶你學(xué)Android:Canvas類全面解析
Carson帶你學(xué)Android:Path類全面解析


目錄

示意圖

1. getMeasuredWidth() / getMeasuredHeight()返回值

1.1 結(jié)論

返回的值是 View在Measure過程中測量的寬 / 高

1.2 源碼分析

public final int getMeasuredWidth() {  
      return mMeasuredWidth & MEASURED_SIZE_MASK;  
      // getMeasuredWidth()的返回值是mMeasuredWidth(MEASURED_SIZE_MASK = 靜態(tài)常量 = 限制mMeasuredWidth大新耆场)
      // 該值的賦值來源:Measure過程中的setMeasuredDimension() -> 分析1
      //
  }  

/**
  * 分析1:setMeasuredDimension()
  * 作用:存儲(chǔ)測量后的View寬 / 高
  * 注:該方法即為我們重寫onMeasure()所要實(shí)現(xiàn)的最終目的
  **/
    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {  
    //參數(shù)說明:測量后子View的寬 / 高值

        // 特別注意:
        // 將測量后子View的寬 / 高值進(jìn)行傳遞
        // 正是這里,賦值mMeasuredWidth的 = measuredWidth
            mMeasuredWidth = measuredWidth;
            mMeasuredHeight = measuredHeight;  
          
            mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;  
        } 
    // setMeasuredDimension()的參數(shù)measuredWidth 是從getDefaultSize()獲得的 
    // 在onMeasure()里調(diào)用 -> 分析2

/**
  * 分析2:onMeasure()
  * 作用:a. 根據(jù)View寬/高的測量規(guī)格計(jì)算View的寬/高值:getDefaultSize()
  *      b. 存儲(chǔ)測量后的View寬 / 高:setMeasuredDimension()
  **/ 
  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {  
    // 參數(shù)說明:View的寬 / 高測量規(guī)格

    // 特別注意苟蹈,正是這句話糊渊,下面我們繼續(xù)看getDefaultSize()的介紹 -> 分析3
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),  
                         getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));  
}

/**
  * 分析3:getDefaultSize()
  * 作用:根據(jù)View寬/高的測量規(guī)格計(jì)算View的寬/高值
  **/
  public static int getDefaultSize(int size, int measureSpec) {  

        // 參數(shù)說明:
        // size:提供的默認(rèn)大小
        // measureSpec:寬/高的測量規(guī)格(含模式 & 測量大小)

            // 設(shè)置默認(rèn)大小
            int result = size; 
            
            // 獲取寬/高測量規(guī)格的模式 & 測量大小
            int specMode = MeasureSpec.getMode(measureSpec);  
            int specSize = MeasureSpec.getSize(measureSpec);  
          
            switch (specMode) {  
                // 模式為UNSPECIFIED時(shí)汉操,使用提供的默認(rèn)大小 = 參數(shù)Size
                case MeasureSpec.UNSPECIFIED:  
                    result = size;  
                    break;  

                // 模式為AT_MOST,EXACTLY時(shí)再来,使用View測量后的寬/高值 = measureSpec中的Size
                case MeasureSpec.AT_MOST:  
                case MeasureSpec.EXACTLY:  
                    result = specSize;  
                    break;  
            }  

         // 返回View的寬/高值
            return result;  
    } 

2. getWidth() / getHeight()返回值

2.1 結(jié)論

返回的值是 View在Layout過程中的寬 / 高,即最終的寬 / 高

2.2 源碼分析

public final int getWidth() {  
      return mRight - mLeft;  
      // mRight、mLeft的值賦值是在layout過程中的setFrame()->分析1
  }  

  /**
  * 分析1:setFrame()
  * 作用:根據(jù)傳入的4個(gè)位置值采缚,設(shè)置View本身的四個(gè)頂點(diǎn)位置
  * 即:最終確定View本身的位置
  */ 
  protected boolean setFrame(int left, int top, int right, int bottom) {
        ...
    // 特別注意:就是這里賦值mRight针炉、mLeft的
    // 通過以下賦值語句記錄下了視圖的位置信息,即確定View的四個(gè)頂點(diǎn)
    // 從而確定了視圖的位置
    mLeft = left;
    mTop = top;
    mRight = right;
    mBottom = bottom;

    // setFrame()的參數(shù)left扳抽、right是從在layout()調(diào)用時(shí)傳入的 -> 分析2

    }

/**
  * 分析2:layout()
  * 作用:確定View本身的位置篡帕,即設(shè)置View本身的四個(gè)頂點(diǎn)位置
  */ 
  public void layout(int l, int t, int r, int b) {  

    ... 
      
    // 特別注意
    boolean changed = isLayoutModeOptical(mParent) ?
            setOpticalFrame(l, t, r, b) : setFrame(l, t, r, b);
    // 而l、t贸呢、r镰烧、b,則是傳入的View的四個(gè)頂點(diǎn)邊界值
    // getWidth()的返回值 = mRight - mLeft = r - l = 子View的右邊界 - 子view的左邊界 = 寬
  ...

}   

2.3 總結(jié)

示意圖

3. 應(yīng)用場景

  • getMeasuredWidth() / getMeasuredHeight()是在Measure過程中賦值的楞陷,所以需在Measure過程后獲取的值才有意義
  • 同理怔鳖,getWidth() / getHeight()在Layout過程中賦值,所以在Layout過程后獲取的值才有意義

所以固蛾,二者的應(yīng)用場景是:

  • getMeasuredWidth() / getMeasuredHeight():在onLayout()中獲取View的寬/高
  • getWidth() / getHeight():在除onLayout()外的地方獲取View的寬/高

4. 額外注意

4.1 不相等情況

  • 問:上面提到结执,一般情況下,二者獲取的寬 / 高是相等的艾凯。那么献幔,“非一般” 情況是什么?(即二者不相等)
  • 答:人為設(shè)置:通過重寫View的 layout()強(qiáng)行設(shè)置
@Override
public void layout( int l , int t, int r , int b){
   // 改變傳入的頂點(diǎn)位置參數(shù)
   super.layout(l趾诗,t蜡感,r+100,b+100);
}
  • 效果:在任何情況下铸敏,getWidth() / getHeight()獲得的寬/高 總比 getMeasuredWidth() / getMeasuredHeight()獲取的寬/高大100px
  • 即:View的最終寬/高 總比 測量寬/高 大100px

雖然這樣的人為設(shè)置無實(shí)際意義缚忧,但證明了:View的最終寬 / 高 與 測量寬 / 高是可以不一樣

4.2 辟謠

網(wǎng)上流傳這么一個(gè)原因描述二者的值的關(guān)系:

  • 在當(dāng)屏幕可包裹內(nèi)容時(shí),他們的值是相等的杈笔;
  • 只有當(dāng)view超出屏幕后闪水,才能看出他們的區(qū)別:當(dāng)超出屏幕后getMeasuredWidth() = getWidth() + 屏幕之外沒有顯示的大小,即:getMeasuredWidth()是實(shí)際View的大小蒙具,與屏幕無關(guān)球榆;而getHeight的大小此時(shí)則是屏幕的大小

下面,我用一個(gè)實(shí)例來進(jìn)行辟謠禁筏!

  • 實(shí)例說明:改變按鈕大谐侄ぁ(不超過屏幕 & 超過屏幕),在onWindowFocusChanged()里分別使用getWidth() & getMeasureWidth()獲得按鈕的寬篱昔,以進(jìn)行驗(yàn)證
  • 注:因?yàn)樵趏nWindowFocusChanged()時(shí)每强,View已經(jīng)測量好了,即走完了Measure & Layout過程州刽,所以選擇在此方法中獲取
  • 實(shí)現(xiàn)代碼
public class MainActivity extends AppCompatActivity {

    private Button mBtn;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtn = (Button) findViewById(R.id.button);

    }

    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        System.out.println("mBtn.getWidth() = " + mBtn.getWidth());
        System.out.println( "mBtn.getMeasureWidth() = " + mBtn.getMeasuredWidth());
    }
}

情況1:按鈕大小不超出屏幕

<Button
      android:id="@+id/button"
      android:layout_width="300px"
      android:layout_height="300px"
      android:text="carson的demo"/>

測試結(jié)果:二者是相等的


示意圖
示意圖

情況2:按鈕大小超出屏幕

<Button
      android:id="@+id/button"
      android:layout_width="2000px"
      android:layout_height="300px"
      android:text="carson的demo"/>

測試結(jié)果:二者仍然是相等的空执!


示意圖
示意圖

最終結(jié)論:在非人為設(shè)置的情況下,getWidth() / getHeight()獲得的寬高(View的最終寬/高)與 getMeasuredWidth() / getMeasuredHeight()獲得的寬/高(View的測量寬/高 )永遠(yuǎn)是相等的穗椅。


5. 總結(jié)

  • 下面辨绊,用一張圖總結(jié)getMeasuredWidth() / getMeasuredHeight() 與 getWidth() / getHeight()的區(qū)別:
示意圖

歡迎關(guān)注Carson_Ho的簡書

不定期分享關(guān)于安卓開發(fā)的干貨,追求短匹表、平门坷、快,但卻不缺深度袍镀。


請點(diǎn)贊默蚌!因?yàn)槟愕墓膭?lì)是我寫作的最大動(dòng)力!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末苇羡,一起剝皮案震驚了整個(gè)濱河市敏簿,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌宣虾,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件温数,死亡現(xiàn)場離奇詭異绣硝,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)撑刺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進(jìn)店門鹉胖,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事甫菠∧硬” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵寂诱,是天一觀的道長拂苹。 經(jīng)常有香客問我,道長痰洒,這世上最難降的妖魔是什么瓢棒? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮丘喻,結(jié)果婚禮上脯宿,老公的妹妹穿的比我還像新娘。我一直安慰自己泉粉,他們只是感情好连霉,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著嗡靡,像睡著了一般跺撼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上叽躯,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天财边,我揣著相機(jī)與錄音,去河邊找鬼点骑。 笑死酣难,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的黑滴。 我是一名探鬼主播憨募,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼袁辈!你這毒婦竟也來了菜谣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤晚缩,失蹤者是張志新(化名)和其女友劉穎尾膊,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體荞彼,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡冈敛,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸣皂。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片抓谴。...
    茶點(diǎn)故事閱讀 38,161評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暮蹂,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出癌压,到底是詐尸還是另有隱情仰泻,我是刑警寧澤,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布滩届,位于F島的核電站集侯,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏丐吓。R本人自食惡果不足惜浅悉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望券犁。 院中可真熱鬧术健,春花似錦、人聲如沸粘衬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衙吩。三九已至荚孵,卻和暖如春嫡意,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背饲握。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工奈懒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留遭商,地道東北人屯阀。 一個(gè)月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓缅帘,卻偏偏與公主長得像,于是被迫代替她去往敵國和親难衰。 傳聞我的和親對象是個(gè)殘疾皇子钦无,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評論 2 344

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