获取View宽高的几种方式


我们经常需要在程序中拿到View的宽高,但有些时候,无论我们调用getHeight(),getWidth(),或者调用getMeasureHeight(),或者getMeasureWidth(),获取到的宽和高都是0,例如在onCreate,onStart或者View还没有添加到视图中,下面就介绍几种获取宽高的方式: 1.最简单也是最low比的方式:延时 这是都知道的方法,既然拿不到,我们就等View添加到视图中再拿。通常都是这样写:

               new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    int height = companyHeaderComponent.getHeight();
                    int width = companyHeaderComponent.getWidth();
                    Log.e("Hwidth: "+width,"Hheight: "+height);
                }
            },200);

12-04 22:40:49.912 5741-5741/com.sankuai.moviepro E/Hwidth: 1440: Hheight: 981

标准的延时获取,获取到的结果没问题,有问题是究竟延时多少才合适呢,我们也不确定,给个大概。这样延时还有可能造成内存上的泄露,如果延时期间页面已经销毁了,就可能异常,所以这种方式不推荐。

2.不指定父布局宽高,手动调用measure

            int w = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
            int h = View.MeasureSpec.makeMeasureSpec(0,View.MeasureSpec.UNSPECIFIED);
            companyHeaderComponent.measure(w,h);
            int height = companyHeaderComponent.getMeasuredHeight();
            int width = companyHeaderComponent.getMeasuredWidth();
            Log.e("width: "+width,"height: "+height);

12-04 22:46:28.912 8480-8480/com.sankuai.moviepro E/width: 1053: height: 981

咦?这里获取的高度没什么问题,但是宽度好像少了一点,这种方法获取到的宽高并不准确,因为这种方式计算宽高是完全不考虑父布局的大小,并且手动调用了measure导致measure多执行了一次,这种方式的优点就是,都不需要View添加到ViewGroup就可以提前知道View的宽高。

3.使用PreDrawListener

            ViewTreeObserver viewTreeObserver = companyHeaderComponent.getViewTreeObserver();
            viewTreeObserver.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener(){
                @Override
                public boolean onPreDraw() {
                    int height = companyHeaderComponent.getMeasuredHeight();
                    int width = companyHeaderComponent.getMeasuredWidth();
                    Log.e("width: "+width,"height: "+height);
                    return true;
                }
            });

12-05 01:20:21.470 13151-13151/com.sankuai.moviepro E/width: 1440: height: 981

PreDraw的调用时间是,当一个视图树将要绘制时调用这个回调函数,这也就说明已经执行过measure和layout,在draw之前,但注意该方法会多次调用,会在每一次绘制之前都会调用。

4.使用GlobalLayoutListener

            ViewTreeObserver observer = companyHeaderComponent.getViewTreeObserver();
            observer.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                @Override
                public void onGlobalLayout() {
                    companyHeaderComponent.getViewTreeObserver().removeOnGlobalLayoutListener(this);
                    Log.e("width: "+companyHeaderComponent.getWidth(),"Hheight: "+ companyHeaderComponent.getHeight());
                }
            });

12-05 01:27:01.563 22653-22653/com.sankuai.moviepro E/width: 1440: Hheight: 981

onGlobalLayout的调用时间是,当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时调用这个回调函数。避免频繁调用,推荐这种方式获取宽高。

5.在onWindowFocusChanged获取

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if(hasFocus){
        Log.e("width: "+companyHeaderComponent.getWidth(),"height: "+ companyHeaderComponent.getHeight());
    }
}

重写Activity中的onWindowFocusChanged,当Activity获取到焦点的时候View已经绘制完成,也能获取到View的准确宽高了。同样的Dialog和PopupWindow也可以在这里弹出,需要注意的是这个方法会调用多次,当hasFocus为true时,才可进行相应的操作。

以后可不要再说 怎么拿到的宽高又是0啊。。。



本作品由 Tony Zhang 创作,采用 CC BY-NC-SA 3.0 许可协议 进行许可。

Comments