关于ViewTreeObserver的理解

来源:互联网 发布:淘宝评价时间是多久 编辑:程序博客网 时间:2024/05/29 18:02
作用:通过名字就可以知道它是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变小。
0 0
原创粉丝点击