关于Activity中获取View宽高的那些事
作者:互联网
关于Activity中获取View宽高的那些事
现在有一个业务需求,需要在Activity已启动的时候做一个任务,该任务需要获取某个View的宽/高。直接的想法是从Activity的onCreate或者onResume里面去获取这个View的宽/高。但是实际上在onCreate、onStart、onResume中均无法获取正确的宽/高信息。
原因在于View的Measure过程和Activity的生命周期方法不是同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时某个View已经测量完毕,如果View没有测量完毕,那么获得的宽高就是0。
解决这个问题的方法有四个。
- Activity/View#onWindowFocusChanged
onWindowFocusChanged():当Activity的窗口得到焦点和失去焦点时均会被调用,且会被调用多次。这个方法被调用时,View已经初始化完毕,宽/高已经准备好了,这个时候去获取宽/高是没有问题的。典型的代码如下。
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
if (hasFocus) {
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
}
- view.post(runnable)
通过post可以将一个runnable投递到一个消息队列的尾部,然后等待Looper调用此runnable的时候,View也已经初始化好了。典型代码如下。
protected void onStart() {
super.onStart();
view.post(new Runnable() {
@Override
public void run() {
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
})
}
- ViewTreeObserver
ViewTreeObserver:注册一个观察者来监听视图树,当视图树的布局、视图树的焦点、视图树将要绘制、视图树滚动等发生改变时,ViewTreeObserver都会收到通知,ViewTreeObserver不能被实例化,可以调用View.getViewTreeObserver()来获得。
使用ViewTreeObserver的众多回调可以解决这个问题,比如使用OnGlobalLayoutListener接口,当View树的状态发生改变或者View树内部的View的可见性发生改变,onGlobalLayoutListener方法会被回调。值得注意的是,**伴随着View树状态改变等,onGlobalLayoutListener会被调用多次。**典型代码如下。
protected void onStart() {
super.onStart();
ViewTreeObserver observer = view.getViewTreeObserver();
observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
@SuppressWarnings("deprecation")
@Override
public void onGlobalLayout() {
view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int width = view.getMeasureWidth();
int height = view.getMeasureHeight();
}
})
}
- view.measure(int widthMeasureSpec, int heightMeasureSpec)
通过手动对View进行measure来得到View的宽/高。按照View的LayoutParams来分:
1)match_parent
直接放弃,无法measure出具体的宽/高。原因在于构造此中MeasureSpec需要知道parentSize,即父容器的剩余空间,而此时是无法知道parentSize的。
2)具体数值(dp/px)
以宽高为100px为例子,具体的代码实现如下。
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
view.measure(widthMeasureSpec, heightMeasureSpec);
3)wrap_content
具体代码实现如下。
int widthMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30) - 1, MeasureSpec.AT_MOST);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec((1<<30) - 1, MeasureSpec.AT_MOST);
view.measure(widthMeasureSpec, heightMeasureSpec);
这里的(1<<30)-1,通过分析MeasureSpec可以知道,View的尺寸使用30位二进制表示,在最大化模式下,用最大值(1<<30)-1在理论上是合理的。
参考资料:《Android开发艺术探索》
标签:MeasureSpec,int,宽高,Activity,ViewTreeObserver,view,View 来源: https://blog.csdn.net/weixin_38196407/article/details/105889992