关于Activity中获取View宽高的那些事

关于Activity中获取View宽高的那些事

  现在有一个业务需求,需要在Activity已启动的时候做一个任务,该任务需要获取某个View的宽/高。直接的想法是从Activity的onCreate或者onResume里面去获取这个View的宽/高。但是实际上在onCreate、onStart、onResume中均无法获取正确的宽/高信息
在这里插入图片描述

  原因在于View的Measure过程和Activity的生命周期方法不是同步执行的,因此无法保证Activity执行了onCreate、onStart、onResume时某个View已经测量完毕,如果View没有测量完毕,那么获得的宽高就是0
在这里插入图片描述

  解决这个问题的方法有四个。

  1. Activity/View#onWindowFocusChanged

  onWindowFocusChanged():当Activity的窗口得到焦点和失去焦点时均会被调用,且会被调用多次。这个方法被调用时,View已经初始化完毕,宽/高已经准备好了,这个时候去获取宽/高是没有问题的。典型的代码如下。

public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        int width = view.getMeasureWidth();
        int height = view.getMeasureHeight();
    }
}
  1. 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();
        }
    })
}
  1. 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();
        }
    })
}
  1. 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开发艺术探索》

原创文章 78 获赞 25 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_38196407/article/details/105889992
今日推荐