一、前言

一般获取 View 宽高,会使用View.getWidth、View.getHeight()、View.getMeasuredWidth() 、View.getgetMeasuredHeight()来获得某个view的宽度或高度。但是在界面 onCreate(),onStart(),onResume()方法直接获取都会得到 0,原因是这几个生命周期内 View还没绘制完成。不过还是有方法去获取宽高的,下面是几种比较好用的方法去获取宽高。

1.1 在 post 方法中获取,推荐使用

这里的view可以是你需要获取宽高的View。要注意的是view要执行此方法必须保证它已经attached到了window上,因此在此之前是不能调用这个方法的。而通过view.post()在主线程的消息队列尾部插入了一个消息,也就是说执行获取宽高的操作被延后了,并且能够保证Measure操作在此之前,所以就能够在这里获取到正确的宽高了。Runnable 对象中的方法会在 View 的 measure()、layout() 等事件完成后触发。
UI 事件队列会按顺序处理事件,在 setContentView() 被调用后,事件队列中会包含一个要求重新 layout 的 message,所以任何 post 到队列中的 Runnable 对象都会在 Layout 发生变化后执行。
优点:该方法只会执行一次,且逻辑简单,建议使用。此方法保证获取到的宽高是准确的;
缺点:不能及时获取到,实际上还是把操作延后了,需要在Runnable里再执行相应回调。
这种方法比较简单,我个人比较喜欢,示例如下:

  1. // tv_scroll_title_one.post {
  2. // val height = tv_scroll_title_one.height
  3. // LogUtils.e("height=$height")//height=750
  4. // }

1.2 使用ViewTreeObserver的方式

在视图将要绘制时调用该监听事件,会被调用多次,因此获取到视图的宽度和高度后要移除该监听事件。这同样是 ViewTreeObserver 的接口。OnPreDrawListener是在draw之前的回调,此时已经 layout 过,可以获取到 View 的宽高值。OnPreDrawListener还可以控制绘制流程,返回false的时候就取消当前绘制流程,View会再schedule下一次绘制:

  1. //方法二
  2. tv_scroll_title_one.viewTreeObserver.addOnPreDrawListener {
  3. //不做处理会一直重复调用,调用一次就够了
  4. if (!hasMeasured){
  5. val height = tv_scroll_title_one.height
  6. LogUtils.e("height=$height")//height=750
  7. hasMeasured = true
  8. }
  9. true//返回true为可用状态
  10. }

1.3 在onWindowFocusChanged中获取

当整个DecorView已经绘制完成,Activty已经要显示出来的时候,就会回调Activity#onWindowFocusChanged。Activity的窗口得到焦点时,View已经初始化完成,此时获取到的View的宽高是准确的,此方法会被多次调用,当Activity的窗口得到/失去焦点时均会被调用一次,同样的Dialog和PopupWindow也可以在这里弹出,需要注意的是这个方法会调用多次,当hasFocus为true时,才可进行相应的操作。

  1. override fun onWindowFocusChanged(hasFocus: Boolean) {
  2. super.onWindowFocusChanged(hasFocus)
  3. //方法三,会重复调用,当Activity的窗口得到焦点和失去焦点时均会被调用一次,如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用,不太适合处理一些复杂的业务逻辑
  4. val height = tv_scroll_title_one.height
  5. LogUtils.e("height=$height")
  6. }

1.4 OnLayoutChangeListener获取,推荐

在视图的 layout 改变时调用该事件,会被多次调用,因此需要在获取到视图的宽度和高度后执行 remove 方法移除该监听事件。

  1. view.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
  2. @Override
  3. public void onLayoutChange(View v, int left, int top, int right, int bottom,
  4. int oldLeft, int oldTop, int oldRight, int oldBottom) {
  5. view.removeOnLayoutChangeListener(this);
  6. int w = view.getWidth();
  7. int h = view.getHeight();
  8. }
  9. });

Android 在onCreate()方法中获取控件宽高值为0解决方案
Android获取View宽高的常见方式