转载于:Android 中获取控件宽和高的方法(详细解析) - CSDN博客 https://blog.csdn.net/CodeIsPoisonous/article/details/54316025
Android 中获取控件宽和高的方法
第一种:直接获取getWidth()和getHeight()
我们都知道这两个方法在onCreate()方法中得到的数据都是0;
- 1
- 2
代码:
mTextView = (TextView) findViewById(R.id.textview);
mWidth = mTextView.getWidth();
mHeight = mTextView.getHeight();
Log.d(TAG, "onCreate: width = " + mWidth + " height = " + mHeight);
- 1
- 2
- 3
- 4
- 5
布局:
<TextView
android:id="@+id/textview"
android:layout_width="200dp"
android:layout_height="200dp"
android:background="#33ff00ff"
android:text="Hello World!"/>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
log打印:
05:31:47.982 25750-25750/? D/MainActivity: onCreate: width = 0 height = 0
- 1
- 2
onResume()方法中得到的也是零;
代码:
@Override
protected void onResume() {
super.onResume();
mWidth = mTextView.getWidth();
mHeight = mTextView.getHeight();
Log.d(TAG, "onResume: width = " + mWidth + " height = " + mHeight);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
log打印:
05:31:47.982 25750-25750/? D/MainActivity: onResume: width = 0 height = 0
- 1
- 2
onStart()就不用多说了;我们都知道getWidth()和getHeight(),只有在View布局完成之后才会有值,那么在Activity的生命周期中的哪个方法中会有值呢?
答案是:
onWindowFocusChanged(boolean hasFocus){}:当Activity的焦点发生改变时调用,他会在onResume()方法执行之后调用,Activity的生命周期方法与 View的绘制流程方法的执行顺序到底是怎样的呢?参见大神博客点击跳转得到的结论是:
oncreate()→onResume()→onMeasure()→onLayout()→onWidnowFocusChanged()→.....→onDraw()...
- 1
- 2
可以看到onWindowFocusChanged()方法是在onLayout()之后执行的,所以getWidth()与getHeight()会得到具体的数值,测试代码如下:
@Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
mWidth = mTextView.getWidth();
mHeight = mTextView.getHeight();
Log.d(TAG, "onWindowFocusChanged: width = " + mWidth + " height = " + mHeight);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
log打印:
D/MainActivity: onWindowFocusChanged: width = 600 height = 600
- 1
- 2
控件的实际宽高就是600,记住这个答案!!!(为什么不是200!不明白的可以看一下屏幕密度,dp与px相关的知识)
第二种:手动测量(自己调用measure()方法)
代码:
mTextView = (TextView) findViewById(R.id.textview);
mTextView.measure( 0, 0);
int measuredWidth = mTextView.getMeasuredWidth();
int measuredHeight = mTextView.getMeasuredHeight();
Log.d(TAG, "onCreate: measuredWidth =" + measuredWidth + " measuredHeight = " + measuredHeight);
- 1
- 2
- 3
- 4
- 5
- 6
布局和之前一样,log打印如下:
D/MainActivity: onCreate: measuredWidth =225 measuredHeight = 57
- 1
- 2
什么鬼? 看到这个结果我又返回去看了一眼布局
->android:layout_width=”200dp”
->android:layout_height=”200dp”
没毛病啊,这个225和57哪来的?
接下来,将布局中的HelloWorld *2,改成
android:text="Hello World!Hello World!"
- 1
- 2
log打印:
onCreate: measuredWidth =450 measuredHeight = 57
- 1
- 2
对的,你猜的没错,他得到的是内容的大小!!跟你给控件设置的宽高没一毛钱关系(感兴趣的可以自己测一下,我把TextView改成ImageView,宽和高都给的很小或者很大,设置个大的图片,getMeasuredWidth()方法得到的结果都是图片的大小)
所以,此方法慎用!!!
第三种:观察者模式
观察View的绘制流程,设置OnGlobalLayoutListener监听,布局完成时会调用onGlobalLayout(),在onGlobalLayout()方法里面获取空间的宽和高
代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textview);
mTextView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mWidth = mTextView.getWidth();
int measuredHeight = mTextView.getMeasuredHeight();
mHeight = mTextView.getHeight();
Log.d(TAG, "onGlobalLayout: width = " + mWidth + " height = " + mHeight + "***" + measuredHeight);
}
});
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
log打印:
onGlobalLayout: width = 600 height = 600
- 1
- 2
第四种:View.post(Runnable action)
代码:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.textview);
mTextView.post(new Runnable() {
@Override
public void run() {
mWidth = mTextView.getWidth();
mHeight = mTextView.getHeight();
Log.d(TAG, "run: width = " + mWidth + " height = " + mHeight);
}
});
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
log打印:
run: width = 600 height = 600
- 1
- 2
解释一下为什么View.post(Runnable action)可以获取到正确的宽高
先看一下方法执行的时间
这里用自定义控件,重写onMeasure(),onLayout(),onDraw().方法
在这些方法中分别测量宽高,并打印时间
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
mWidth = getWidth();
mHeight = getHeight();
Log.d(TAG, "onMeasure: width = " + mWidth + " height = " + mHeight + "时间 : " + System.currentTimeMillis());
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
mWidth = getWidth();
mHeight = getHeight();
Log.d(TAG, "onLayout: width = " + mWidth + " height = " + mHeight + "时间 : " + System.currentTimeMillis());
super.onLayout(changed, left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
mWidth = getWidth();
mHeight = getHeight();
Log.d(TAG, "onDraw: width = " + mWidth + " height = " + mHeight + "时间 : " + System.currentTimeMillis() );
super.onDraw(canvas);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
log打印:
onResume: width = 0 height = 0时间 : 1484040644793
onMeasure: width = 0 height = 0时间 : 1484040644819
onLayout: width = 600 height = 600时间 : 1484040644904
run: width = 600 height = 600时间 : 1484040644930
onWindowFocusChanged: width = 600 height = 600时间 : 1484040644930
onDraw: width = 600 height = 600时间 : 1484040644971
- 1
- 2
- 3
- 4
- 5
- 6
- 7
从上面日志的时间可以看出,View.post()中的run方法和onWindowFocusChanged方法几乎是同时执行,都是在onLayout之后,所以都是可以得到控件的宽高的!!!
还有,getMeasuredHeight()和getMeasuredWidth(),在Layout执行完成后得到的也是控件的宽高了!!!
<link rel="stylesheet" href="https://csdnimg.cn/release/phoenix/template/css/markdown_views-ea0013b516.css">
</div>