Android如何获取View的宽高

1,050 阅读1分钟

因为View的measure过程和Activity的生命周期方法不是同步执行的。虽然都是在主线中执行,但并没有明确的时序。

1. Activity/View#onWindowFocusChanged

View初始化完成会回调onWindowFocusChanged()方法。

2. view.post(runnable)

通过post可以将runnable投递到消息队列的尾部,然后等待Looper调用此runnable()的时候,View也已经初始化完成了。

3. ViewTreeObserver

使用ViewTreeObserver的众多回调可以完成这个功能。View中有ViewTreeObserver类的实例,获取该对象然后添加OnGlobalLayoutListener监听,当View树的状态发生改变或者View树内部的View的可见性发生变化时,onGlobalLayout()方法将被回调,因此这是获取View的宽高的一个好的时机。

protected void onStart() {
    super.onStart();
    ViewTreeObserver observer = view.getViewTreeObserver();
    observer.adddOnGlobalLayoutListener(new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            view.getViewTreeObserver().removeGlobalOnLayoutListener(this);
            int width = view.gerMeasureWidth();
            int height = view.getMeasureHeight();
        }
    });
}

4. view.measure(int widthMeasureSpec, int heightMeasureSpec)

通过手动对View进行measure来得到View的宽高。需要分情况。

  • match_parent 直接放弃,无法measure出具体的宽高。原因是根据View的measure过程,构造此种MeasureSpec需要知道parentSize,即父容器的剩余空间大小,而这个时候我们无法知道parentSize的值,所以理论上不可能测量出View的大小。
  • 具体dp/px 比如宽高都是100px,如下measure:
int widthMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
view.measure(widthMeasureSpec, heightMeasureSpec);
  • wrap_content

如下measure:

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位二进制表示,也就是最大是30个1(即2^30 -1),也就是(1 << 30) -1,在最大化模式下,我们用View理论上能支持的最大值去构造MeasureSpec是合理的。