ViewTreeObserver的使用

序言

A view tree observer is used to register listeners that can be notified of global changes in the view tree. Such global events include, but are not limited to, layout of the whole tree, beginning of the drawing pass, touch mode change…. A ViewTreeObserver should never be instantiated by applications as it is provided by the views hierarchy. Refer to View.getViewTreeObserver() for more information.
官方文档的描述ViewTreeObserver是用来监听一些全局变化的。

在 ViewTreeObserver 中,包含了以下几个接口:
interface ViewTreeObserver.OnGlobalFocusChangeListener
interface ViewTreeObserver.OnGlobalLayoutListener
interface ViewTreeObserver.OnPreDrawListener
interface ViewTreeObserver.OnScrollChangedListener
interface ViewTreeObserver.OnTouchModeChangeListener
本文将测试除 ViewTreeObserver.OnScrollChangedListener 外的四个接口

xml布局如下

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:gravity="center"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:id="@+id/layout"
    tools:context="trs.com.viewtreeobserverdemo.MainActivity">
    <TextView
        android:id="@+id/tv_show"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <EditText
        android:hint="et1"
        android:tag="et1"
        android:id="@+id/et_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <EditText
        android:tag="et2"
        android:hint="et2"
        android:layout_marginTop="10dp"
        android:id="@+id/et_2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:text="test"
        android:id="@+id/btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
</LinearLayout>

让MainActivity实现相应接口

public class MainActivity extends AppCompatActivity implements ViewTreeObserver.OnGlobalLayoutListener, ViewTreeObserver.OnPreDrawListener, ViewTreeObserver.OnGlobalFocusChangeListener, ViewTreeObserver.OnTouchModeChangeListener, View.OnClickListener

在onCreat中添加监听

  EditText et_1,et_2;
    TextView tv_show;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewTreeObserver vto = findViewById(R.id.layout).getViewTreeObserver();
        et_1= (EditText) findViewById(R.id.et_1);
        et_2= (EditText) findViewById(R.id.et_2);
        vto.addOnGlobalLayoutListener(this);
        vto.addOnPreDrawListener(this);
        vto.addOnGlobalFocusChangeListener(this);
        vto.addOnTouchModeChangeListener(this);
        findViewById(R.id.btn).setOnClickListener(this);
        tv_show= (TextView) findViewById(R.id.tv_show);
    }

一.OnGlobalFocusChangeListener

首先测试ViewTreeObserver.OnGlobalFocusChangeListener,实现接口方法

代码

    @Override
    public void onGlobalFocusChanged(View oldFocus, View newFocus) {
        if(oldFocus!=null) {
            tv_show.setText("Focus change from " + oldFocus.getTag() + " to " + newFocus.getTag());
        }else{
            tv_show.setText( newFocus.getTag()+"get focus");
        }
    }

注意:在第一次进入页面的时候没有oldFoucs

效果

这个接口很简单就是监听focus的变化。

这里写图片描述

二.OnPreDrawListener

OnPreDrawListener接口是在绘制界面前调用

代码

   @Override
    public boolean onPreDraw() {
        et_1.setHint("set hint on onPreDraw ");
        //Return true to proceed with the current drawing pass, or false to cancel.
        //返回 true 继续绘制,返回false取消。
        return true;
    }

效果

这里写图片描述

如果返回false的话,效果是这样的,界面没有绘制。
这里写图片描述

关于OnPreDrawListener的使用,有一个例子就是CoordinatorLayout调用Behavior的onDependentViewChanged就是通过注册OnPreDrawListener接口,在绘制的时候检查界面是否发生变化,如果变化就调用Behavior的onDependentViewChanged。

源码

 @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        resetTouchBehaviors();
        if (mNeedsPreDrawListener) {
            if (mOnPreDrawListener == null) {
                mOnPreDrawListener = new OnPreDrawListener();
            }
            //注册OnPreDrawListener
            final ViewTreeObserver vto = getViewTreeObserver();
            vto.addOnPreDrawListener(mOnPreDrawListener);
        }
        if (mLastInsets == null && ViewCompat.getFitsSystemWindows(this)) {
            // We're set to fitSystemWindows but we haven't had any insets yet...
            // We should request a new dispatch of window insets
            ViewCompat.requestApplyInsets(this);
        }
        mIsAttachedToWindow = true;
    }

  class OnPreDrawListener implements ViewTreeObserver.OnPreDrawListener {
        @Override
        //分发OnDependentViewChanged
            dispatchOnDependentViewChanged(false);
            return true;
        }
    }

 void dispatchOnDependentViewChanged(final boolean fromNestedScroll) {
        final int layoutDirection = ViewCompat.getLayoutDirection(this);
        final int childCount = mDependencySortedChildren.size();
        for (int i = 0; i < childCount; i++) {
            final View child = mDependencySortedChildren.get(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();

            // Check child views before for anchor
            for (int j = 0; j < i; j++) {
                final View checkChild = mDependencySortedChildren.get(j);

                if (lp.mAnchorDirectChild == checkChild) {
                    offsetChildToAnchor(child, layoutDirection);
                }
            }
            //判断是否发生变化
            // Did it change? if not continue
            final Rect oldRect = mTempRect1;
            final Rect newRect = mTempRect2;
            getLastChildRect(child, oldRect);
            getChildRect(child, true, newRect);
            if (oldRect.equals(newRect)) {
                continue;
            }
            recordLastChildRect(child, newRect);

            // Update any behavior-dependent views for the change
            for (int j = i + 1; j < childCount; j++) {
                final View checkChild = mDependencySortedChildren.get(j);
                final LayoutParams checkLp = (LayoutParams) checkChild.getLayoutParams();
                final Behavior b = checkLp.getBehavior();

                if (b != null && b.layoutDependsOn(this, checkChild, child)) {
                    if (!fromNestedScroll && checkLp.getChangedAfterNestedScroll()) {
                        // If this is not from a nested scroll and we have already been changed
                        // from a nested scroll, skip the dispatch and reset the flag
                        checkLp.resetChangedAfterNestedScroll();
                        continue;
                    }
                //调用onDependentViewChanged
                    final boolean handled = b.onDependentViewChanged(this, checkChild, child);

                 ...
                }
            }
        }
    }

三.OnGlobalLayoutListener

Interface definition for a callback to be invoked when the global layout state
 or the visibility of views within the view tree changes.

当在一个视图树中全局布局发生改变或者视图树中的某个视图的可视状态发生改变时,所要调用的回调函数的接口类

代码

1.在点击时改变EditText的可视性。

    @Override
    public void onClick(View v) {
        if(et_1.isShown()){
            et_1.setVisibility(View.GONE);
        }else{
            et_1.setVisibility(View.VISIBLE);
        }
    }

2.在onGlobalLayout显示EditText的可见性

  @Override
    public void onGlobalLayout() {
                if(et_1.isShown()){
                    tv_show.setText("EditText1 显示");
                }else{
                    tv_show.setText("EditText1 隐藏");
                }
    }

效果

这里写图片描述

注意:在测试的时候发现使用

et_1.setVisibility(View.INVISIBLE);

时并不会触发OnGlobalLayoutListener而只能使用

  et_1.setVisibility(View.GONE);

补充

可以使用OnGlobalLayoutListener获取控件宽高。

private int mHeaderViewHeight;
private View mHeaderView;

.....

mHeaderView.getViewTreeObserver().addOnGlobalLayoutListener(
    new OnGlobalLayoutListener() {
        @Override
        public void onGlobalLayout() {

            mHeaderViewHeight = mHeaderView.getHeight();
            mHeaderView.getViewTreeObserver()
                    .removeGlobalOnLayoutListener(this);
        }
});

关于OnTouchModeChangeListener的使用目前还没有搞清楚,以后再回来补充吧。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值