一、理解ViewTreeObserver概念
ViewTreeObserver用来注册监听器,在视图树全局发生变化时收到通知。它不能被应用实例化,因为它是由视图提供,通过android.view.View#getViewTreeObserver()来获取。
ViewTree:视图树。在Android中,所有视图由View和View的子类组成。ViewGroup也是view的子类,它是View的容器,它可以装载View和ViewGroup。这样ViewGroup和View以树形结构一层一层的嵌套组合,就形成了视图树。
Observer:观察者。使用了观察者的设计模式,在这里,即ViewTree时被观察者,ViewTreeObserver是观察者,通过ViewTreeObserver注册监听来观察ViewTree的变化,当ViewTree发生变化,就会调用ViewTreeObserver的相关方法来通知其这一改变。我们可以在ViewTreeObserver中add自己的监听器,从而得到ViewTree的某一变化的通知做出自己的逻辑处理。
二、如何获取ViewTreeObserver
ViewTreeObserver是不能被应用程序实例化的,因为它是由视图提供的,通过view.getViewTreeObserver()获取。
//View.java
public ViewTreeObserver getViewTreeObserver() {
if (mAttachInfo != null) {
return mAttachInfo.mTreeObserver;
}
if (mFloatingTreeObserver == null) {
mFloatingTreeObserver = new ViewTreeObserver();
}
return mFloatingTreeObserver;
}
其中,AttachInfo是View的一个内部类。当一个View附着到它的父Window中时,这个View能获取到一组View和父Window之间的信息,就存储在AttachInfo当中。AttachInfo中就有一个ViewTreeObserver对象。当mAttachInfo为空时,返回mFloatingTreeObserver,一个特殊的ViewTreeObserver。
那么,每一个view的mAttachInfo是怎么获取到的呢?AttachInfo是在View第一次attach到Window时,ViewRoot传给自己的子View的,然后沿着视图树,AttachInfo会一直传递到每一个View。
//ViewGroup.java
@Override
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
...
final int count = mChildrenCount;
final View[] children = mChildren;
for (int i = 0; i < count; i++) {
final View child = children[i];
child.dispatchAttachedToWindow(info,
combineVisibility(visibility, child.getVisibility()));
}
...
}
//View.java
void dispatchAttachedToWindow(AttachInfo info, int visibility) {
//System.out.println("Attached! " + this);
mAttachInfo = info;
...
}
另外,当新的View加入到ViewGroup中时,也会将AttachInfo传入。
//ViewGroup.java
private void addViewInner(View child, int index, LayoutParams params,
boolean preventRequestLayout) {
...
AttachInfo ai = mAttachInfo;
if (ai != null && (mGroupFlags & FLAG_PREVENT_DISPATCH_ATTACHED_TO_WI