自定义view android获取宽高,Android自定义View:你知道通过getWidth() 与 getMeasuredWidth() 获取宽高的区别吗?...

2a11c76f5711

前言

在自定义View的过程中,使用getMeasuredWidth() / getMeasuredHeight() 与 getWidth() / getHeight()都能获取View的宽 / 高,但是二者有什么区别呢?

今天,我将深入源码,给大家分析二者之间的区别,希望你们会喜欢。

目录

2a11c76f5711

示意图

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

1.1 结论

返回的值是 View在Measure过程中测量的宽 / 高

1.2 源码分析

由于getMeasuredWidth()与getMeasuredHeight()同理,下面只讲解getMeasuredWidth()

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;

}

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

2.1 结论

返回的值是 View在Layout过程中的宽 / 高,即最终的宽 / 高

2.2 源码分析

由于getWidth()与getHeight()同理,下面只讲解getWidth()。

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的左边界 = 宽

...

}

2.3 总结

2a11c76f5711

示意图

3. 应用场景

getMeasuredWidth() / getMeasuredHeight()是在Measure过程中赋值的,所以需在Measure过程后获取的值才有意义

同理,getWidth() / getHeight()在Layout过程中赋值,所以在Layout过程后获取的值才有意义

所以,二者的应用场景是:

getMeasuredWidth() / getMeasuredHeight():在onLayout()中获取View的宽/高

getWidth() / getHeight():在除onLayout()外的地方获取View的宽/高

4. 额外注意

4.1 不相等情况

问:上面提到,一般情况下,二者获取的宽 / 高是相等的。那么,“非一般” 情况是什么?(即二者不相等)

答:人为设置:通过重写View的 layout()强行设置

@Override

public void layout( int l , int t, int r , int b){

// 改变传入的顶点位置参数

super.layout(l,t,r+100,b+100);

}

效果:在任何情况下,getWidth() / getHeight()获得的宽/高 总比 getMeasuredWidth() / getMeasuredHeight()获取的宽/高大100px

即:View的最终宽/高 总比 测量宽/高 大100px

虽然这样的人为设置无实际意义,但证明了:View的最终宽 / 高 与 测量宽 / 高是可以不一样

4.2 辟谣

网上流传这么一个原因描述二者的值的关系:

在当屏幕可包裹内容时,他们的值是相等的;

只有当view超出屏幕后,才能看出他们的区别:当超出屏幕后getMeasuredWidth() = getWidth() + 屏幕之外没有显示的大小,即:getMeasuredWidth()是实际View的大小,与屏幕无关;而getHeight的大小此时则是屏幕的大小

下面,我用一个实例来进行辟谣!

实例说明:改变按钮大小(不超过屏幕 & 超过屏幕),在onWindowFocusChanged()里分别使用getWidth() & getMeasureWidth()获得按钮的宽,以进行验证

注:因为在onWindowFocusChanged()时,View已经测量好了,即走完了Measure & Layout过程,所以选择在此方法中获取

实现代码

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:按钮大小不超出屏幕

android:id="@+id/button"

android:layout_width="300px"

android:layout_height="300px"

android:text="carson的demo"/>

测试结果:二者是相等的

2a11c76f5711

示意图

2a11c76f5711

示意图

情况2:按钮大小超出屏幕

android:id="@+id/button"

android:layout_width="2000px"

android:layout_height="300px"

android:text="carson的demo"/>

测试结果:二者仍然是相等的!

2a11c76f5711

示意图

2a11c76f5711

示意图

最终结论:在非人为设置的情况下,getWidth() / getHeight()获得的宽高(View的最终宽/高)与 getMeasuredWidth() / getMeasuredHeight()获得的宽/高(View的测量宽/高 )永远是相等的。

5. 总结

下面,用一张图总结getMeasuredWidth() / getMeasuredHeight() 与 getWidth() / getHeight()的区别:

2a11c76f5711

示意图

请点赞!因为你们的赞同/鼓励是我写作的最大动力!

不定期分享关于安卓开发的干货,追求短、平、快,但却不缺深度。

2a11c76f5711

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值