ViewTreeObserver是什么
从命名上来看是一个观察者,根据Android上面观察者模式的使用(可以参考BaseAdapter.notifyDataSetChanged()方法),用来监听一些行为,当这些行为发生改变的时候就会通知ViewTreeObserver,然后做相应的一些处理了。
从ViewRootImpl里面的踪迹可以寻出一些调用的信息,调用了dispatchOnScrollChanged、dispatchOnWindowAttachedChange等等。其实不需要从源码里面知道怎么调用了。就像Activity的onCreate不需要知道源码里面是怎么调用的。
ViewTreeObserver监听的内容
ViewTreeObserver监听的一些行为。
addOnDrawListener() // api 16 当View即将被绘制的时候会被调用,这个时候不能再改变View的属性了。在Demo中这个方法一直不会被执行。
addOnGlobalFocusChangeListener() // api 1 当全局焦点改变的时候会执行。
addOnGlobalLayoutListener() // api 1 当布局状态或者显示隐藏的时候就会执行。
addOnPreDrawListener() // api 1 当View即将被绘制的时候会被调用,这个时候可以改变View的属性,重新布局。
addOnScrollChangedListener() // api 3 当View回调的时候就会执行。
addOnTouchModeChangeListener() // api 1 Touch Model代表的是用户的是触摸模式,当用户的键盘模式和touch model切换的时候就会执行。
addOnWindowAttachListener() // api 18 当View attch到窗口的时候或者View detached出窗口的时候就会执行。
addOnWindowFocusChangeListener() // api 18 当窗口焦点发生变化的时候就会执行。
不知道你会不会有一个疑问,是addOnWindowAttachListener中的onWindowAttached先执行还是View的onAttachedToWindow先执行?
Demo
Demo自定义一个View实现关键的回调方法(onMeasure、onLayout、onDraw、onAttachedToWindow等等),Activity实现关键的回调方法(onCreate、onResume、onAttachedToWindow等等),然后为自定义View添加ViewTreeObserver的回调。
第一次进入时的Log。
02-18 14:22:02.255 9874-9874/com.egos.samples E/Egos: onCreate
02-18 14:22:02.255 9874-9874/com.egos.samples E/Egos: onStart
02-18 14:22:02.255 9874-9874/com.egos.samples E/Egos: onResume
02-18 14:22:02.435 9874-9874/com.egos.samples E/Egos: Activity onAttachedToWindow
02-18 14:22:02.435 9874-9874/com.egos.samples E/Egos: View onAttachedToWindow
02-18 14:22:02.435 9874-9874/com.egos.samples E/Egos: onWindowAttached // 在Activity和View执行完以后才执行
02-18 14:22:02.435 9874-9874/com.egos.samples E/Egos: onTouchModeChanged
02-18 14:22:02.435 9874-9874/com.egos.samples E/Egos: View onMeasure
02-18 14:22:02.665 9874-9874/com.egos.samples E/Egos: View onMeasure
02-18 14:22:02.665 9874-9874/com.egos.samples E/Egos: View onLayout
02-18 14:22:02.665 9874-9874/com.egos.samples E/Egos: onGlobalLayout // 在onMeasure和onLayout执行完以后才执行
02-18 14:22:02.665 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:22:02.665 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:22:02.665 9874-9874/com.egos.samples E/Egos: View onDraw
02-18 14:22:02.835 9874-9874/com.egos.samples E/Egos: Activity onWindowFocusChanged
02-18 14:22:02.835 9874-9874/com.egos.samples E/Egos: View onWindowFocusChanged
02-18 14:22:02.835 9874-9874/com.egos.samples E/Egos: onWindowFocusChanged
02-18 14:22:02.845 9874-9874/com.egos.samples E/Egos: View onMeasure
02-18 14:22:02.845 9874-9874/com.egos.samples E/Egos: View onLayout
02-18 14:22:02.845 9874-9874/com.egos.samples E/Egos: onGlobalLayout
02-18 14:22:02.845 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:22:02.845 9874-9874/com.egos.samples E/Egos: View onDraw
改变View的颜色Log
02-18 14:23:27.865 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:27.875 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:27.965 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:28.035 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:28.035 9874-9874/com.egos.samples E/Egos: View onDraw
02-18 14:23:28.085 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:28.095 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:28.115 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:28.135 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:23:28.425 9874-9874/com.egos.samples E/Egos: onPreDraw
改变View的大小Log。
02-18 14:24:10.575 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.595 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.615 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.645 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.665 9874-9874/com.egos.samples E/Egos: View onMeasure
02-18 14:24:10.665 9874-9874/com.egos.samples E/Egos: View onLayout
02-18 14:24:10.665 9874-9874/com.egos.samples E/Egos: onGlobalLayout
02-18 14:24:10.665 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.665 9874-9874/com.egos.samples E/Egos: View onDraw
02-18 14:24:10.685 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.685 9874-9874/com.egos.samples E/Egos: View onDraw
02-18 14:24:10.705 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.715 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.745 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:10.765 9874-9874/com.egos.samples E/Egos: onPreDraw
02-18 14:24:11.015 9874-9874/com.egos.samples E/Egos: onPreDraw
从上面的Log应该是能回答上面的问题的,是addOnWindowAttachListener中的onWindowAttached先执行还是View的onAttachedToWindow先执行?其实都是在Activity、View执行完以后才执行的。还能看出什么问题呢?
注意问题
- 对于ViewTreeObserver使用完以后应该及时的removeListener(当然也有一些特殊的情况还是),因为如果不清掉的话可能会执行非常多次。
使用实例
测量键盘高度JKeyboardPanelSwitch
获取一些信息。如View的宽高、TextView的lineCount等等。
// 获取TextView的行数。
text.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener(){
@Override
public boolean onPreDraw(){
text.getViewTreeObserver().removeOnPreDrawListener(this);
lineCount = text.getLineCount();
return true;
}
});