作用:通过名字就可以知道它是View树的观察者,当View树的发生变化的时候会发出通知。ViewTreeObserver是不能被应用程序实例化的,因为它是由视图提供的,通过view.getViewTreeObserver()获取。
熟悉观察者模式的人应该很容易想到,为了能够相应的通知,我们肯定需要注册监听。下面来看看我们可以注册哪些监听。
1、当在一个视图树中的焦点状态或者可见性发生改变时调用OnGlobalFocusChangeListener的onGlobalFocusChanged()函数。
public void addOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener listener)
1
1
2、当在一个视图树中的焦点状态或者可见性发生改变时调用OnGlobalFocusChangeListener的onGlobalFocusChanged()函数。
public void addOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener listener)
1
1
3、当一个视图树将要绘制时调用OnPreDrawListener的onPreDraw()函数
public void addOnPreDrawListener (ViewTreeObserver.OnPreDrawListener listener)
1
1
4、当一个视图发生滚动时调用OnScrollChangedListener的onScrollChanged()函数
public void addOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener listener)
1
1
5、当一个触摸模式发生改变时调用OnTouchModeChangeListener的onTouchModeChanged()函数
public void addOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener listener)
1
1
6、当一个视图树绘制时调用OnDrawListener的onDraw()函数
addOnDrawListener(ViewTreeObserver.OnDrawListener listener)
1
1
7、当View树绑定到window上的时候回调OnWindowAttachListener的onWindowAttached() 函数,当它从window上解绑时调用OnWindowAttachListener的onWindowDetached()
addOnWindowAttachListener(ViewTreeObserver.OnWindowAttachListener listener)
1
1
8、当window的焦点状态发生改变时,调用OnWindowFocusChangeListener的onWindowFocusChanged函数
addOnWindowFocusChangeListener(ViewTreeObserver.OnWindowFocusChangeListener listener)
1
1
对应这些注册的监听,还有相应的删除监听
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)
public void removeOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener victim)
public void removeOnPreDrawListener (ViewTreeObserver.OnPreDrawListener victim)
public void removeOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener victim)
public void removeOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener victim)
public void removeOnDrawListener(ViewTreeObserver.OnDrawListener victim)
public void removeOnWindowAttachListener(ViewTreeObserver.OnWindowAttachListener victim)
public void removeOnWindowFocusChangeListener(ViewTreeObserver.OnWindowFocusChangeListener victim)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上面注册的监听都是View树发生变化的时候,会被自动触发回调,如果我们希望手动触发回调,可以调用下面函数
public final void dispatchOnGlobalLayout ()
手动触发OnGlobalLayoutListener的onGlobalLayout()函数回调
public final boolean dispatchOnPreDraw ()
手动触发OnPreDrawListener的onPreDraw()函数的回调
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
另外还是一个指示当前的ViewTreeObserver是否可用的函数。
public boolean isAlive ()
当observer不可用时,任何方法的调用(除了这个方法)都将抛出一个异常。
1
2
3
1
2
3
应用场景1:获取View的宽高
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyImageView myImageView = (MyImageView) findViewById(R.id.imageview);
int height = 0;
int width = 0 ;
ViewTreeObserver vto = myImageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int height = myImageView.getMeasuredHeight();
int width = myImageView.getMeasuredWidth();
return true;
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
因为回调OnPreDrawListener的onPreDraw,表示这个View准备进行绘制,在绘制之前,这个View的宽高肯定是已经测量好了,所以这个时机是可以得到view的宽高的。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyImageView myImageView = (MyImageView) findViewById(R.id.imageview);
int height = 0;
int width = 0 ;
ViewTreeObserver vto = myImageView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
myImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height = myImageView.getHeight();
int width = myImageView.getWidth();
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
当view的可见状态发生变化的时候回调OnGlobalLayoutListener的onGlobalLayout()函数,这个时候View的宽高肯定也是已经测量好了,所以这个时机是可以得到view的宽高的。
应用场景2:Activity跳转动画
ViewTreeObserver还可以用来监听根布局,用来实现Activity跳转动画,核心代码如下:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
rootView = findViewById(R.id.root);
if (savedInstanceState == null) {
rootView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
startRootAnimation();
return true;
}
});
}
}
因为在绘制之前会触发OnPreDrawListener的onPreDraw()函数,这个时候可以执行跳转动画。
应用场景3:测量软键盘状态和高度
ViewTreeObserver.OnGlobalLayoutListener mListener = new ViewTreeObserver
.OnGlobalLayoutListener() {
public void onGlobalLayout() {
Rect r1 = new Rect();
root.getWindowVisibleDisplayFrame(r1);
Log.e("TAG",r1.bottom+"") ;
}
};
root.getViewTreeObserver().addOnGlobalLayoutListener(mListener);
在根布局加入GlobalLayoutListener监听,通过getWindowVisibleDisplayFrame方法可以观察可见区域的变化,键盘打开后 会影响可见区域的大小,导致Rect的底部r1.bottom变小。
熟悉观察者模式的人应该很容易想到,为了能够相应的通知,我们肯定需要注册监听。下面来看看我们可以注册哪些监听。
1、当在一个视图树中的焦点状态或者可见性发生改变时调用OnGlobalFocusChangeListener的onGlobalFocusChanged()函数。
public void addOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener listener)
1
1
2、当在一个视图树中的焦点状态或者可见性发生改变时调用OnGlobalFocusChangeListener的onGlobalFocusChanged()函数。
public void addOnGlobalLayoutListener (ViewTreeObserver.OnGlobalLayoutListener listener)
1
1
3、当一个视图树将要绘制时调用OnPreDrawListener的onPreDraw()函数
public void addOnPreDrawListener (ViewTreeObserver.OnPreDrawListener listener)
1
1
4、当一个视图发生滚动时调用OnScrollChangedListener的onScrollChanged()函数
public void addOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener listener)
1
1
5、当一个触摸模式发生改变时调用OnTouchModeChangeListener的onTouchModeChanged()函数
public void addOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener listener)
1
1
6、当一个视图树绘制时调用OnDrawListener的onDraw()函数
addOnDrawListener(ViewTreeObserver.OnDrawListener listener)
1
1
7、当View树绑定到window上的时候回调OnWindowAttachListener的onWindowAttached() 函数,当它从window上解绑时调用OnWindowAttachListener的onWindowDetached()
addOnWindowAttachListener(ViewTreeObserver.OnWindowAttachListener listener)
1
1
8、当window的焦点状态发生改变时,调用OnWindowFocusChangeListener的onWindowFocusChanged函数
addOnWindowFocusChangeListener(ViewTreeObserver.OnWindowFocusChangeListener listener)
1
1
对应这些注册的监听,还有相应的删除监听
public void removeGlobalOnLayoutListener (ViewTreeObserver.OnGlobalLayoutListener victim)
public void removeOnGlobalFocusChangeListener (ViewTreeObserver.OnGlobalFocusChangeListener victim)
public void removeOnPreDrawListener (ViewTreeObserver.OnPreDrawListener victim)
public void removeOnScrollChangedListener (ViewTreeObserver.OnScrollChangedListener victim)
public void removeOnTouchModeChangeListener (ViewTreeObserver.OnTouchModeChangeListener victim)
public void removeOnDrawListener(ViewTreeObserver.OnDrawListener victim)
public void removeOnWindowAttachListener(ViewTreeObserver.OnWindowAttachListener victim)
public void removeOnWindowFocusChangeListener(ViewTreeObserver.OnWindowFocusChangeListener victim)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
上面注册的监听都是View树发生变化的时候,会被自动触发回调,如果我们希望手动触发回调,可以调用下面函数
public final void dispatchOnGlobalLayout ()
手动触发OnGlobalLayoutListener的onGlobalLayout()函数回调
public final boolean dispatchOnPreDraw ()
手动触发OnPreDrawListener的onPreDraw()函数的回调
1
2
3
4
5
6
7
8
1
2
3
4
5
6
7
8
另外还是一个指示当前的ViewTreeObserver是否可用的函数。
public boolean isAlive ()
当observer不可用时,任何方法的调用(除了这个方法)都将抛出一个异常。
1
2
3
1
2
3
应用场景1:获取View的宽高
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyImageView myImageView = (MyImageView) findViewById(R.id.imageview);
int height = 0;
int width = 0 ;
ViewTreeObserver vto = myImageView.getViewTreeObserver();
vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
public boolean onPreDraw() {
int height = myImageView.getMeasuredHeight();
int width = myImageView.getMeasuredWidth();
return true;
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
因为回调OnPreDrawListener的onPreDraw,表示这个View准备进行绘制,在绘制之前,这个View的宽高肯定是已经测量好了,所以这个时机是可以得到view的宽高的。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final MyImageView myImageView = (MyImageView) findViewById(R.id.imageview);
int height = 0;
int width = 0 ;
ViewTreeObserver vto = myImageView.getViewTreeObserver();
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
myImageView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
int height = myImageView.getHeight();
int width = myImageView.getWidth();
}
});
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
当view的可见状态发生变化的时候回调OnGlobalLayoutListener的onGlobalLayout()函数,这个时候View的宽高肯定也是已经测量好了,所以这个时机是可以得到view的宽高的。
应用场景2:Activity跳转动画
ViewTreeObserver还可以用来监听根布局,用来实现Activity跳转动画,核心代码如下:
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
rootView = findViewById(R.id.root);
if (savedInstanceState == null) {
rootView.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
rootView.getViewTreeObserver().removeOnPreDrawListener(this);
startRootAnimation();
return true;
}
});
}
}
因为在绘制之前会触发OnPreDrawListener的onPreDraw()函数,这个时候可以执行跳转动画。
应用场景3:测量软键盘状态和高度
ViewTreeObserver.OnGlobalLayoutListener mListener = new ViewTreeObserver
.OnGlobalLayoutListener() {
public void onGlobalLayout() {
Rect r1 = new Rect();
root.getWindowVisibleDisplayFrame(r1);
Log.e("TAG",r1.bottom+"") ;
}
};
root.getViewTreeObserver().addOnGlobalLayoutListener(mListener);
在根布局加入GlobalLayoutListener监听,通过getWindowVisibleDisplayFrame方法可以观察可见区域的变化,键盘打开后 会影响可见区域的大小,导致Rect的底部r1.bottom变小。