你真的理解android事件分发机制了吗

最近在看一些源代码的时候,又遇到了android的事件分发机制,以前我以为我懂了,但是看着看着又糊涂了,于上浏览了下别人写的文章,越看越糊涂,干脆自己写个程序验证下,这一验证,才发现以前没有真正懂这个流程,今天我就用事实说话,来跟大家一起验证下android的事件分发机制(很多坑需要注意):
事件分发中我们无非会遇到这么几个函数,dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent,onTouch,onClick,onLongClick,先从一个简单的View说起,然后再给这个View外面嵌套一个ViewGroup:
一 先看一个简单的View:

<?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:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.fq.myviewgroup.MainActivity">

    <com.fq.myviewgroup.MyTestView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:text="Hello World!"
        android:layout_gravity="center"
        android:gravity="center"
        android:background="#123321"/>
</LinearLayout>
package com.fq.myviewgroup;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

/**
 * Created by fq on 2016/9/21.
 */
public class MyTestView extends TextView implements View.OnClickListener,View.OnLongClickListener,View.OnTouchListener{
    public static final String TAG = "fuqiang";
    public MyTestView(Context context, AttributeSet attrs) {

        super(context, attrs);
        Log.e(TAG, "MyTestView");
        setOnClickListener(this);
        setOnLongClickListener(this);
        setOnTouchListener(this);
    }
    @Override
    public void onClick(View v) {
        Log.e(TAG,"MyTestView ---onClick");
    }

    @Override
    public boolean onLongClick(View v) {
        Log.e(TAG,"MyTestView ---onLongClick");
        return false;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyTestView ---dispatchTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyTestView ---dispatchTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyTestView ---dispatchTouchEvent -- ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyTestView ---onTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyTestView ---onTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyTestView ---onTouchEvent -- ACTION_UP");
                break;
        }
       return super.onTouchEvent(event);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyTestView ---onTouch -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyTestView ---onTouch -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyTestView ---onTouch -- ACTION_UP");
                break;
        }
        return false;
    }
}

这里写图片描述
好了,程序很简单,在各个touch函数打印了相关log,下面我们先来做第一个操作,点击TextView,长按,然后松开,执行结果如下:

E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onLongClick
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---onTouch -- ACTION_UP
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---onClick

以此执行了dispatchTouchEvent,onTouch,onTouchEvent ,onLongClick,onClick,这里我们要注意
onTouch是在onTouchEvent 前执行的,然后执行了长按onLongClick,最后当手抬起的时候,才会执行onClick。
接下来我们第二个操作,点击TextView,移动,然后松开:

E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---onTouch -- ACTION_UP
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---onClick

也很清晰,只是长按没有了而已,接下来我们对代码进行一些改动,让onTouch返回true:

 @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyTestView ---onTouch -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyTestView ---onTouch -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyTestView ---onTouch -- ACTION_UP");
                break;
        }
        return true;
    }

接下来点击,移动,松开:

E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---onTouch -- ACTION_UP

可以看到onTouchEvent 没有被执行了,这是为什么呢,看下dispatchTouchEvent 的源码:

ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }

如果onTouch的返回值是true,直接返回了,不继续执行onTouchEvent了。
看另外一个坑,现在把如下代码注释掉:

//        setOnClickListener(this);
//        setOnLongClickListener(this);

再次按下,移动,松开:

E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_DOWN

移动和抬起全都没有监听到了,这是为什么呢,查看源码发现,设置上面这两句会使onTouchEvent的返回值为true,onTouchEvent的返回值为true才会消费接下来的一系列事件,如果是默认(false),是不会继续消费移动和抬起事件的。

好了,接下来我给这个TextView外面再套一层我写的ViewGroup,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<com.fq.myviewgroup.MyLinearLayout 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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.fq.myviewgroup.MainActivity">

    <com.fq.myviewgroup.MyTestView
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:text="Hello World!"
        android:layout_gravity="center"
        android:gravity="center"
        android:background="#123321"/>
</com.fq.myviewgroup.MyLinearLayout>
package com.fq.myviewgroup;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;

/**
 * Created by fq on 2016/9/21.
 */
public class MyLinearLayout extends LinearLayout implements View.OnClickListener,View.OnLongClickListener,View.OnTouchListener{
    public static final String TAG = "fuqiang";
    public MyLinearLayout(Context context, AttributeSet attrs) {

        super(context, attrs);
        Log.e(TAG , "MyLinearLayout");
//        setOnClickListener(this);
//        setOnLongClickListener(this);
          setOnTouchListener(this);
    }
    @Override
    public void onClick(View v) {
        Log.e(TAG,"MyLinearLayout ---onClick");
    }

    @Override
    public boolean onLongClick(View v) {
        Log.e(TAG,"MyLinearLayout ---onLongClick");
        return false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---onInterceptTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---onInterceptTouchEvent -- ACTION_UP");
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---dispatchTouchEvent -- ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    public boolean onTouch(View v, MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---onTouch -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---onTouch -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---onTouch -- ACTION_UP");
                break;
        }
        return false;
    }
    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept)
    {
        Log.e(TAG, "requestDisallowInterceptTouchEvent ");
        super.requestDisallowInterceptTouchEvent(disallowIntercept);
    }
}

也很简单,就是打印了关键函数的一些log,onClick和onLongClick刚才分析过了,这里直接注释掉,不解释了,接下来,我依然在TextView这个区域按下,移动,抬起,执行结果如下:

E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_DOWN

执行次序,父类的dispatchTouchEvent ,onInterceptTouchEvent ,子类的dispatchTouchEvent ,onTouch ,onTouchEvent ,然后又重新回到父类的onTouch ,onTouchEvent ,并且只监听了ACTION_DOWN,移动和抬起事件没人管了,这是为什么,因为无论是父类还是子类,onTouchEvent 默认都返回的false,也就是说都不管接下来发生的事,按下事件从父类传到子类,又从子类传到父类,没人愿意管,好,下面我们修改父类的onTouchEvent ,让它返回true:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_UP");
                break;
        }
        return true;
    }

然后再按下,移动,抬起,执行结果:

E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_UP

可以看到先执行父类的dispatchTouchEvent ,onInterceptTouchEvent ,然后传到子类,发现子类的onTouchEvent 返回false,表示他不打算管,然后又传回给父类onTouchEvent ,父类onTouchEvent 返回true了,表示要管,所以接下来的移动和抬起事件都传递给父类的dispatchTouchEvent ,onTouch ,onTouchEvent 方法,这时不会再传递给onInterceptTouchEvent ,onInterceptTouchEvent 只负责对子类的分发。
接下来我把子类的onTouchEvent返回值改成True:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyTestView ---onTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyTestView ---onTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyTestView ---onTouchEvent -- ACTION_UP");
                break;
        }
       return true;
    }

然后,按下,移动,抬起:

E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---onTouch -- ACTION_UP
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_UP

可以看到子类消费了接下来的事件,onInterceptTouchEvent 每次还会执行,因为是对子类的分发,最后执行完并不会再回到父类,因为自己已经把剩余事件给消费了。
接下来我们代码不动,进行另外一种操作,我在TextView之外的区域,点击,移动,抬起,执行结果如下:

E/fuqiang: MyLinearLayout
E/fuqiang: MyTestView
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_UP

可以看到,并没有分发给子类,而是自己消费了,是因为按下的区域并没有找到子View,那如果我把父类的onTouchEvent返回值改成默认呢?:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---onTouchEvent -- ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }

在同样在TextView之外的区域按下,移动,抬起

E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_DOWN

由于这个父类onTouchEvent返回值是默认(false),他不打算管接下来的事件,所以就只监听到Down事件了。
接下来我来修改onInterceptTouchEvent,让ACTION_DOWN的时候直接返回true:

 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN");
                return true;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---onInterceptTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---onInterceptTouchEvent -- ACTION_UP");
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

然后在TextView所在区域按下,移动,抬起:

E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_UP

可以看到,父类拦截了接下来的所有操作,并没有传递给子类,我们如果想让父类拦截,在onInterceptTouchEvent按下,移动和抬起事件返回true即可,但是如果明明被拦截了,子类依然想执行怎么办,getParent().requestDisallowInterceptTouchEvent(true),这样调用即可,之前父类的拦截就失效了,但是只能在onInterceptTouchEvent的移动事件ACTION_MOVE之后返回true才行,在ACTION_DOWN里返回true,子类这样写不起作用:

@Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        int action = event.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                getParent().requestDisallowInterceptTouchEvent(true);
                Log.e(TAG,"MyTestView ---dispatchTouchEvent -- ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyTestView ---dispatchTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                getParent().requestDisallowInterceptTouchEvent(false);
                Log.e(TAG,"MyTestView ---dispatchTouchEvent -- ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(event);
    }

再按下,移动,抬起,如下:

E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---onInterceptTouchEvent -- ACTION_DOWN
E/fuqiang: requestDisallowInterceptTouchEvent 
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouch -- ACTION_DOWN
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouch -- ACTION_MOVE
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: requestDisallowInterceptTouchEvent 
E/fuqiang: MyTestView ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyTestView ---onTouch -- ACTION_UP
E/fuqiang: MyTestView ---onTouchEvent -- ACTION_UP

我们看到父类的拦截不起作用了,子类仍然可以监听到后续的操作。

来说最后一个,如果我在父类的dispatchTouchEvent里返回true会怎么样呢:

public boolean dispatchTouchEvent(MotionEvent ev) {
        int action = ev.getAction();
        switch (action){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG,"MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN");
                return true;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG,"MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.e(TAG,"MyLinearLayout ---dispatchTouchEvent -- ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

在case MotionEvent.ACTION_DOWN:的时候返回了true,依然是按下,移动,抬起:

E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_DOWN
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_MOVE
E/fuqiang: MyLinearLayout ---dispatchTouchEvent -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouch -- ACTION_UP
E/fuqiang: MyLinearLayout ---onTouchEvent -- ACTION_UP

可以看到dispatchTouchEvent 监听完DOWN之后,DOWN就此结束了,onInterceptTouchEvent 和onTouch ,onTouchEvent 都没有监听Down了,并且后续的移动和抬起onInterceptTouchEvent 也不执行了,而是父类的dispatchTouchEvent ,onTouch ,onTouchEvent 依次执行。

好了,android事件分发机制就聊到这里,有人会说你为什么不从源码角度去分析下,我想说的是这个东西本来就很多坑了,你这个时候记住了,用的时候可能又会忘了,知道怎么用就好,再分析下源码,估计头更大了,如有问题,欢迎指正,谢谢。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值