前言
- 在自定義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 源碼分析
- 由于getMeasuredWidth()與getMeasuredHeight()同理纠脾,下面只講解getMeasuredWidth()
- 請務(wù)必先了解自定義View的Measure過程:自定義View Measure過程 - 最易懂的自定義View原理系列(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 源碼分析
- 由于getWidth()與getHeight()同理磷瘤,下面只講解getWidth()芒篷。
- 請務(wù)必先了解自定義View的Layout過程:自定義View Layout過程 - 最易懂的自定義View原理系列(3)
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ū)別:
- 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類全面解析
歡迎關(guān)注Carson_Ho的簡書
不定期分享關(guān)于安卓開發(fā)的干貨,追求短匹表、平门坷、快,但卻不缺深度袍镀。