Moosphan / Android-Daily-Interview

:pushpin:每工作日更新一道 Android 面试题,小聚成河,大聚成江,共勉之~

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

2019-08-28:谈一谈获取View宽高的几种方法?

MoJieBlog opened this issue · comments

2019-08-28:谈一谈获取View宽高的几种方法?

view.post一个就行了 一把梭

1.OnGlobalLayoutListener获取
2.OnPreDrawListener获取
3.OnLayoutChangeListener获取
4.重写View的onSizeChanged()
5.使用View.post()方法

使用MessageQueue.addIdleHandler()也可以获取,而且可以防止多次调用。

使用View.post()方法
Context.runOnUiThread(new Runnable()
OnGlobalLayoutListener获取
OnPreDrawListener获取
OnLayoutChangeListener获取
重写View的onSizeChanged()

先说下问题的考点:

首先,在 view 测量过后就可以获得 view 的测量宽高,另外系统可能需要多次 measure 才能确定最终的测量宽高。
其次,由于 view 的测量过程和 Activity 的生命周期不是同步,所以无法保证在 onCreate,onStart, onResume 时已经测量完毕,如果没有测量完毕获取的宽高都是0。

能获取真实 view 宽高的方法:

  • Activity/View 的 onWindowFocusChanged 方法: 方法含义是 View 已经初始完毕,宽高已经准备好了, 得到焦点和失去焦点会被多次调用
  • view.post:将 Runnable 投递到消息队列的尾部,当执行到的时候,view 已经初始化好了
  • ViewTreeObserver: 使用 ViewTreeObserver 众多回调可以完成这功能,比如 onGlobalLayoutListener,会调用多次
  • view.addOnLayoutChangeListener

1 通过 looper 队列

/**
 * 通过 looper 队列
 */
private void getHeight1() {
    tvText.post(new Runnable() {
        @Override
        public void run() {
            int height = tvText.getHeight();
            Log.i(TAG, "MainActivity;getHeight1;height=" + height);
        }
    });
}

2 当视图树的布局发生改变时

/**
 * 当视图树的布局发生改变时
 */
private void getHeight2() {
    tvText.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {
            tvText.getViewTreeObserver().removeOnGlobalLayoutListener(this);

            int height = tvText.getHeight();
            Log.i(TAG, "MainActivity;getHeight2;height=" + height);
        }
    });
}

3 当视图树的进行绘制时

/**
 * 当视图树的进行绘制时
 */
private void getHeight3() {
    tvText.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            tvText.getViewTreeObserver().removeOnPreDrawListener(this);

            int height = tvText.getHeight();
            Log.i(TAG, "MainActivity;getHeight3;height=" + height);
            return false;
        }
    });
}

4 view 布局改变时

/**
 * view 布局改变时
 */
private void getHeight4() {
    tvText.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
        @Override
        public void onLayoutChange(View v, int left, int top, int right, int bottom, int oldLeft, int oldTop, int oldRight, int oldBottom) {
            tvText.removeOnLayoutChangeListener(this);
            int height = tvText.getHeight();
            Log.i(TAG, "MainActivity;getHeight4;height=" + height);
        }
    });
}

通过view.post Runnable方法里面通过view.getHeight

commented

看了上方的回答,发现“答不对题”;题目是:获取View宽高的几种方法;回答应该是获取方法,而上文回答的全是”获取时机“

  1. 获取时机:上文都说过了。而且都是能获取到的时机。其实获取不到的时候,判断一下,等有的时候,再存内存里面就可以了。
  2. 获取方法:
    a. getWidth() 和 getHeight() 方法,本质上是等view宽高计算结束,取内存的值。
    b. getMeasureWidth 和 getMeasureHeight 方法,本质上是measure结束,取内存的值。
    c. View 的宽高本身是可以写死的。那么就可以直接 取值。任何时机结果都是对的。【例如铺满横屏,有时候可以取屏幕宽度】
    d. 取业务缓存。这个依据特定场景,例如:同一个手机,上下滑动直播间,共用的同一个Activity,那么界面的高度,是可以复用的。即:第一次获取完成之后,就可以一直用这个高度值。