Carson带你学Android自定义View文章系列: Carson带你学Android:自定义View基础 Carson带你学Android:一文梳理自定义View工作流程 Carson带你学Android:自定义View Measure过程 Carson带你学Android:自定义View Layout过程 Carson带你学Android:自定义View Draw过程 Carson带你学Android:手把手教你写一个完整的自定义View Carson带你学Android:Canvas类全面解析 Carson带你学Android:Path类全面解析
返回的值是 View在Measure过程中测量的宽 / 高
public final int getMeasuredWidth() {
return mMeasuredWidth & MEASURED_SIZE_MASK;
// getMeasuredWidth()的返回值是mMeasuredWidth(MEASURED_SIZE_MASK = 静态常量 = 限制mMeasuredWidth大小)
// 该值的赋值来源:Measure过程中的setMeasuredDimension() -> 分析1
//
}
/**
* 分析1:setMeasuredDimension()
* 作用:存储测量后的View宽 / 高
* 注:该方法即为我们重写onMeasure()所要实现的最终目的
**/
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
//参数说明:测量后子View的宽 / 高值
// 特别注意:
// 将测量后子View的宽 / 高值进行传递
// 正是这里,赋值mMeasuredWidth的 = measuredWidth
mMeasuredWidth = measuredWidth;
mMeasuredHeight = measuredHeight;
mPrivateFlags |= PFLAG_MEASURED_DIMENSION_SET;
}
// setMeasuredDimension()的参数measuredWidth 是从getDefaultSize()获得的
// 在onMeasure()里调用 -> 分析2
/**
* 分析2:onMeasure()
* 作用:a. 根据View宽/高的测量规格计算View的宽/高值:getDefaultSize()
* b. 存储测量后的View宽 / 高:setMeasuredDimension()
**/
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
// 参数说明:View的宽 / 高测量规格
// 特别注意,正是这句话,下面我们继续看getDefaultSize()的介绍 -> 分析3
setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
/**
* 分析3:getDefaultSize()
* 作用:根据View宽/高的测量规格计算View的宽/高值
**/
public static int getDefaultSize(int size, int measureSpec) {
// 参数说明:
// size:提供的默认大小
// measureSpec:宽/高的测量规格(含模式 & 测量大小)
// 设置默认大小
int result = size;
// 获取宽/高测量规格的模式 & 测量大小
int specMode = MeasureSpec.getMode(measureSpec);
int specSize = MeasureSpec.getSize(measureSpec);
switch (specMode) {
// 模式为UNSPECIFIED时,使用提供的默认大小 = 参数Size
case MeasureSpec.UNSPECIFIED:
result = size;
break;
// 模式为AT_MOST,EXACTLY时,使用View测量后的宽/高值 = measureSpec中的Size
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
result = specSize;
break;
}
// 返回View的宽/高值
return result;
}
返回的值是 View在Layout过程中的宽 / 高,即最终的宽 / 高
public final int getWidth() {
return mRight - mLeft;
// mRight、mLeft的值赋值是在layout过程中的setFrame()->分析1
}
/**
* 分析1:setFrame()
* 作用:根据传入的4个位置值,设置View本身的四个顶点位置
* 即:最终确定View本身的位置
*/
protected boolean setFrame(int left, int top, int right, int bottom) {
...
// 特别注意:就是这里赋值mRight、mLeft的
// 通过以下赋值语句记录下了视图的位置信息,即确定View的四个顶点
// 从而确定了视图的位置
mLeft = left;
mTop = top;
mRight = right;
mBottom = bottom;
// setFrame()的参数left、right是从在layout()调用时传入的 -> 分析2
}
/**
* 分析2:layout()
* 作用:确定View本身的位置,即设置View本身的四个顶点位置
*/
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的四个顶点边界值
// getWidth()的返回值 = mRight - mLeft = r - l = 子View的右边界 - 子view的左边界 = 宽
...
}
所以,二者的应用场景是:
@Override
public void layout( int l , int t, int r , int b){
// 改变传入的顶点位置参数
super.layout(l,t,r+100,b+100);
}
虽然这样的人为设置无实际意义,但证明了:View的最终宽 / 高 与 测量宽 / 高是可以不一样
网上流传这么一个原因描述二者的值的关系:
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"/>
测试结果:二者是相等的
情况2:按钮大小超出屏幕
<Button
android:id="@+id/button"
android:layout_width="2000px"
android:layout_height="300px"
android:text="carson的demo"/>
测试结果:二者仍然是相等的!
最终结论:在非人为设置的情况下,getWidth() / getHeight()获得的宽高(View的最终宽/高)与 getMeasuredWidth() / getMeasuredHeight()获得的宽/高(View的测量宽/高 )永远是相等的。