Android 获取View的宽高

获取View的宽高

我们经常会需要获取View的宽度或者高度,但是我们会发现不管是在onCreate还是onStart,onResume里面获取到的值都是0.

这是因为View的measure过程和Activity的生命周期方法不是同步进行的,不能保证在Activity的onCreate,onStart,onResume方法执行时View已经测量完毕,所以不能获得正确宽高值。

因此我们需要通过其他方法来实现,如下几种方法都可以获取正确宽高值

  • Activity/View的onWindowFocusChanged方法体内
  • 利用view.post(runnable)
  • ViewTreeObserver
  • view.measure
  • 自定义View时获取宽高

Activity/View的onWindowFocusChanged方法体内

这个方法的含义是View已经初始化完毕了,自然可以获取宽高,但是该方法有可能多次执行,当Activity焦点变化的时候都会被调用。

public void onWindowFocusChanged(boolean hasFocus){
    super.onWindowFocusChanged(hasFocus);
    if(hasFocus){
        int width = view.getMeasuredWidth();
        int height = view.getMeasuredHeight();
    }
}

view.post(runnable)

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

protected void onStart(){
    super.onStart();
    view.post(new Runnable);
        @Override
        public void run() {
        int width = view.getMeasuredWidth();
        int height = view.getMeasuredHeight();
        }
    });
}

这里在onCreate 和 onResume里面都可以。

ViewTreeObserver

顾名思义这个类是整个View树结构的观察者,通过它的众多回调可以获取到view的宽高,比如onGlobalLayoutListener接口,当View树的状体改变或者View树内部的View的可见性发生改变时该接口都将被回调,显然onGlobal也会被多次调用.

protected void onResume() {
        super.onResume();
        ViewTreeObserver observer = btn.getViewTreeObserver();
        observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                btn.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                int width = btn.getMeasuredWidth();
                int height = btn.getMeasuredHeight();
            }
        });
    }

这里的removeOnGlobalLayoutListener方法需要API 16。

view.measure

该方法有两个参数即宽高的MeasureSpec,完整方法名为measure(int widthMeasureSpec,int heightMesureSpec) 如果对此不了解的可以先去学习关于view的measure过程.
通过手动对view测量得到宽高,这种方法较为复杂,要分情况处理,根据view的LayoutParams来分

  • match_parent
    根据View的measure过程分析构造此种MeasureSpec需要知道父容器的剩余空间,而此时无法知道,所以理论上不可能测出View的大小。
  • wrap_content
    如下
int widthMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
int heightMeasureSpec = View.MeasureSpec.makeMeasureSpec((1 << 30) - 1, View.MeasureSpec.AT_MOST);
view.measure(widthMeasureSpec,heightMeasureSpec);

int width = view.getMeasuredWidth());
int height = view.getMeasuredHeight());

注意到(1 << 30)-1,通过分析MeasureSpec的实现可以知道,View的尺寸使用30位二进制表示,也就是说最大是30个1(即2^30 -1),也就是(1 << 30)-1,在最大化模式下我们用View理论上支持的最大值去构造MeasureSpec是合理的。

另外关于View的measure,网络上有两个错误的用法:
1.

int widthMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
int heightMeasureSpec = MeasureSpec.makeMeasureSpec(-1,MeasureSpec.UNSPECIFIED);
view.measure(widthMeasureSpec,heightMeasureSpec);

2.

view.measure(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT);

自定义View时获取宽高

有时我们在自定义控件的时候会需要用到宽高值,大部分的时候可以在onLayout方法里面获取到真实宽高值,同样也可以用上述第三种方法获取。首先让自定义的View实现onGlobalListener接口,需要实现onGlobalLayout方法.

然后在onAttachedToWindow()添加

    @Override
    protected void onAttachedToWindow() {
        super.onAttachedToWindow();
        getViewTreeObserver().addOnGlobalLayoutListener(this);
    }

记得要在onDetachedFromWindow()删除

    @Override
    protected void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        getViewTreeObserver().removeOnGlobalLayoutListener(this);(API 16)
    }

接着在onGlobalLayout方法中获取宽高即可,因为要调用多次,可以设立个标志位.

    private boolean isFirst = true;
    @Override
    public void onGlobalLayout() {
        if (isFirst) {
            int height = getHeight();
            isFirst = false;
        }
    }

(此文摘录于书籍《Android开发艺术探索》 任玉刚 著
刚哥博客 http://blog.csdn.net/singwhatiwanna/article/details/49230997)

你可能感兴趣的:(Android-常用技巧)