一、前言
一般获取 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里再执行相应回调。
这种方法比较简单,我个人比较喜欢,示例如下:
// tv_scroll_title_one.post {
// val height = tv_scroll_title_one.height
// LogUtils.e("height=$height")//height=750
// }
1.2 使用ViewTreeObserver的方式
在视图将要绘制时调用该监听事件,会被调用多次,因此获取到视图的宽度和高度后要移除该监听事件。这同样是 ViewTreeObserver 的接口。OnPreDrawListener是在draw之前的回调,此时已经 layout 过,可以获取到 View 的宽高值。OnPreDrawListener还可以控制绘制流程,返回false的时候就取消当前绘制流程,View会再schedule下一次绘制:
//方法二
tv_scroll_title_one.viewTreeObserver.addOnPreDrawListener {
//不做处理会一直重复调用,调用一次就够了
if (!hasMeasured){
val height = tv_scroll_title_one.height
LogUtils.e("height=$height")//height=750
hasMeasured = true
}
true//返回true为可用状态
}
1.3 在onWindowFocusChanged中获取
当整个DecorView已经绘制完成,Activty已经要显示出来的时候,就会回调Activity#onWindowFocusChanged。Activity的窗口得到焦点时,View已经初始化完成,此时获取到的View的宽高是准确的,此方法会被多次调用,当Activity的窗口得到/失去焦点时均会被调用一次,同样的Dialog和PopupWindow也可以在这里弹出,需要注意的是这个方法会调用多次,当hasFocus为true时,才可进行相应的操作。
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
//方法三,会重复调用,当Activity的窗口得到焦点和失去焦点时均会被调用一次,如果频繁地进行onResume和onPause,那么onWindowFocusChanged也会被频繁地调用,不太适合处理一些复杂的业务逻辑
val height = tv_scroll_title_one.height
LogUtils.e("height=$height")
}
1.4 OnLayoutChangeListener获取,推荐
在视图的 layout 改变时调用该事件,会被多次调用,因此需要在获取到视图的宽度和高度后执行 remove 方法移除该监听事件。
view.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) {
view.removeOnLayoutChangeListener(this);
int w = view.getWidth();
int h = view.getHeight();
}
});