获取View的宽高——Android工作记录

###0.前言
在Android开发中,许多场景都需要获取指定View的宽高来进行业务操作。但,有些时候在View的宽高不为0的情况下,通过View.getHeight()或者View.getMeasuredHeight()获取到的数值为0。这是为什么呢?

其实,这种情况和Android的界面绘制流程有关,简单来说,就是View的宽高需要在测量(Measure)之后才确定。

###1.获取View的宽高的方法
大多数遇到数值为0的问题的,都是在获取在activity的onCreate中获取。而OnCreate方法的调用过程其实还没完成View的测量工作,所以获取数值为0.

此时,如果还需要获取view的宽高,有3种方式:

  1. 自己调用view的测量方法(view.measure(…)),提前进行测量
  2. 使用ViewTreeObserve.OnGlobalLayoutListener进行视图树的监听,在监听回调中获取
  3. 取巧方式,使用view.post(Runnable)获取(异步获取)

####1.1 通过调用view.measure()实现

/**
 * 测量view
 * @param view
 */
public static void measureView(View view) {
    ViewGroup.LayoutParams p = view.getLayoutParams();
    if (p == null) {
        p = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    int childWidthSpec = ViewGroup.getChildMeasureSpec(0, 0, p.width);
    int lpHeight = p.height;
    int childHeightSpec;
    if (lpHeight > 0) {
        childHeightSpec = View.MeasureSpec.makeMeasureSpec(lpHeight, View.MeasureSpec.EXACTLY);
    } else {
        childHeightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
    }
    view.measure(childWidthSpec, childHeightSpec);
}

/**
 * 获取view的高度
 * @param view
 * @return
 */
public static int getViewHeight(View view) {
    measureView(view);
    return view.getMeasuredHeight();
}

需要注意的是,如果LayoutParams中配置了宽高的值,那么需要传入此数值以保证测量的合理性和准确性。

###1.2 通过ViewTreeObserve.OnGlobalLayoutListener实现
当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时,所要调用的回调函数的接口类

int mHeaderViewHeight;
mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            mHeaderViewHeight = mHeaderView.getHeight();
            getViewTreeObserver()
                    .removeGlobalOnLayoutListener(this);
        }

需要注意的是,OnGlobalLayoutListener会被多次调用,所以在获取宽高后需要移除监听。

###1.3 view.post(Runnable)获取
其实,此方法是一个比较取巧的方式,通过view.post(Runnable)将获取宽高的Runnable放到消息队列中并执行,异步执行了获取宽高的代码。

int mHeaderViewHeight;
mHeaderView.post(new Runnable(){
        @Override
        public void run() {
             mHeaderViewHeight = mHeaderView.getHeight();
        }

});

###2.在自定义控件中获取自身宽高

  • onlayout方法中获取(这个方法是在onMeasure之后的)
  • onSizeChanged也可以获取,这个方法只有在view改变之后才会回调,当然第一次也是会回调的。

###4.总结

  • 在Android获取视图(View)的宽高需要遵循视图树的绘制流程,所以获取宽高需要在view被测量之后。
  • 使用view.measure(0, 0);可以非常简便的获取到view的宽高,但因为无视了view自身的配置和父类传入的限制,实际获取到的宽高可能存在一定的问题。

END

–Nowy

–2019.02.11

分享到