美文网首页
View的事件传递

View的事件传递

作者: 拙峰朽木 | 来源:发表于2017-06-09 18:20 被阅读17次

先创建个ViewGroupA和ViewB,然后我们来看看事件是怎么传递的。

public class ViewGroupA extends RelativeLayout {
 ...
    /**
     * 传递事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("touch", "ViewGroupA : dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);

    }

    /**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("touch", "ViewGroupA :   onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    /**
     * 处理事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("touch", "ViewGroupA :   onTouchEvent");
        return super.onTouchEvent(event);
    }
}

这里没有直接继承ViewGroup 而是RelativeLayout,主要是为好布局。其实一样的RelativeLayout就是继承的ViewGroup。

public class ViewB extends View {
....
  /**
     * 事件传递
     *
     * @param event
     * @return
     */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("touch", "ViewB : dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

/**
     * 事件处理
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("touch", "ViewB : onTouchEvent");
        return super.onTouchEvent(event);
    }

}

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.rongcheng.customview.activity.TouchDispatchActivity">

    <com.rongcheng.customview.customView.ViewGroupA
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_centerInParent="true"
        android:background="@color/colorAccent">

        <com.rongcheng.customview.customView.ViewB
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:layout_centerInParent="true"
            android:background="@color/colorPrimary"/>
    </com.rongcheng.customview.customView.ViewGroupA>
</RelativeLayout>

显示效果:

image.png

点击蓝色区域即ViewB,然后我们看下日志输出:

 D/touch: ViewGroupA : dispatchTouchEvent
 D/touch: ViewGroupA :   onInterceptTouchEvent
 D/touch: ViewB : dispatchTouchEvent
 D/touch: ViewB : onTouchEvent
 D/touch: ViewGroupA :   onTouchEvent

从日志输出顺序可以看出,事件由Activity-->ViewGroupA 如果ViewGroupA不拦截
事件由ViewGroupA-->ViewB 当前ViewB为最上面一层View,如果ViewB不处理,那么ViewB就将事件又返回给ViewGroupB 交由他的onTouchEvent处理

试下ViewGroupA将事件拦截了,看看日志输出:
修改ViewGroupA代码:

    /**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("touch", "ViewGroupA :   onInterceptTouchEvent");
        return true;
    }

日志输出:

 D/touch: ViewGroupA : dispatchTouchEvent
 D/touch: ViewGroupA :   onInterceptTouchEvent
 D/touch: ViewGroupA :   onTouchEvent

我们发现没有关于ViewB的输出,这说明我们的事件拦截成功了,ViewB没有接受到此次事件。
现在我们来看下ViewGroupA输出的顺序:
1.dispatchTouchEvent
2.onInterceptTouchEvent
3.onTouchEvent
我们可以猜测为:事件传递给ViewGroupA,如果ViewGroupA不传递给子View(ViewB),说明那么他就会走onTouchEvent,即为自己处理。

现在我们看下,如果我们拦截后不处理当前事件会怎样:

public class ViewGroupA extends RelativeLayout {
...
/**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    ");
                return true;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP ");
                return false;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_CANCEL ");
                return false;
            default:
                return false;
        }
    }

}

输出日志:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onTouchEvent   MotionEvent.ACTION_DOWN 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_DOWN
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

如果我们拦截后,处理了又怎样,将ViewGroupA中的onTouchEvent的Down事件返回为true:

 /**
     * 处理事件
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewGroupA :   onTouchEvent   MotionEvent.ACTION_DOWN ");
                return true;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewGroupA :   onTouchEvent   MotionEvent.ACTION_UP ");
                break;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewGroupA :   onTouchEvent    MotionEvent.ACTION_CANCEL ");
                break;
        }
        return super.onTouchEvent(event);
    }

显示日志:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onTouchEvent   MotionEvent.ACTION_DOWN 
D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewGroupA :   onTouchEvent   MotionEvent.ACTION_UP 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

综合上面两个输出可以得出结论:
1.如果一个View拦截了,那么这个事件接下来的系列都会由他处理,并且不会调用onInterceptTouchEvent()。

2.如果一个View拦截,那么如果他不处理,那么这个事件接下来的系列就会交由他的父类来处理,即由其父类的onTouchEvent()来处理。

现在在ViewGroupA里不拦截,在ViewB中只处理Down

public class ViewB extends View {
...
/**
     * 事件处理
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewB onTouchEvent:  ACTION_DOWN ");
                return true;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewB onTouchEvent: ACTION_UP ");
                return false;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewB onTouchEvent: ACTION_CANCEL ");
                return false;
            default:
                return false;
        }
    }
}

日志输出:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewB dispatchTouchEvent:  ACTION_DOWN 
D/touch: ViewB onTouchEvent:  ACTION_DOWN 
D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewB dispatchTouchEvent: ACTION_UP 
D/touch: ViewB onTouchEvent: ACTION_UP 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

3.如果View不消耗除Down之外的其实事件,那么这个点击事件会消失,此时父View(VIewGroupA)的onTouchEvent()不会调用,并且当前的VIew可接收到持续事件,最终这些小时的点击事件会传给Activity处理

我们现在不拦截Down,只拦截UP看看会怎样:

public class ViewGroupA extends RelativeLayout {
...
 /**
     * 拦截事件
     *
     * @param ev
     * @return
     */
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    ");
                return false;
            case MotionEvent.ACTION_UP:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP ");
                return true;
            case MotionEvent.ACTION_CANCEL:
                Log.d("touch", "ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_CANCEL ");
                return false;
            default:
                return false;
        }
    }

}

日志输出:

D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_DOWN    
D/touch: ViewB dispatchTouchEvent:  ACTION_DOWN 
D/touch: ViewB onTouchEvent:  ACTION_DOWN 
D/touch: ViewGroupA :   dispatchTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewGroupA :   onInterceptTouchEvent    MotionEvent.ACTION_UP 
D/touch: ViewB dispatchTouchEvent: ACTION_CANCEL 
D/touch: ViewB onTouchEvent: ACTION_CANCEL 
D/touch: TouchDispatchActivity onTouchEvent: MotionEvent.ACTION_UP

我们发现传递了down事件,拦截up事件的话,子View就会走ACTION_CANCEL。

相关文章

网友评论

      本文标题:View的事件传递

      本文链接:https://www.haomeiwen.com/subject/jqpfqxtx.html