Android 事件分发机制

文章详细阐述了Android中触摸事件的分发流程,包括dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent三个关键方法的作用及返回值影响。通过实例分析了不同返回值情况下事件如何在Activity、ViewGroup和View之间传递与处理。同时,对比了有响应能力与无响应能力的控件在事件处理上的区别,并提及了onClick、onTouch和onTouchEvent的执行顺序以及onTouch返回值的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

事件分发机制
一、Touch事件分发中重要方法介绍
1.事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

return false:不继续向下分发该事件,不会执行当前view的onInterceptTouchEvent()和onTouchEvent()方法,会返回至上一级,由上一级继续处理该事件。
return true:不继续向下分发事件,不会执行当前view的onInterceptTouchEvent()和onTouchEvent()方法,事件在当前view的dispatchTouchEvent被处理。
return super.dispatchTouchEvent(ev):向下分发事件,执行当前view的onInterceptTouchEven()

2.事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

return false:对事件不进行拦截,事件继续向下传递,并执行子view的dispatchTouchEvent()。
return true:事件拦截,事件不继续向下传递,事件由当前view的onTouchEvent()执行响应。
return super.onInterceptTouchEvent(ev):与false情况一致。

3.事件响应:public boolean onTouchEvent(MotionEvent event)

return false:不响应事件,返回上级,由上级处理。
return true:响应事件,事件不再继续传递。
return super.onTouchEvent(ev):如果当前view有能力处理,则与true情况一致,例如button控件,默认有能力处理事件,如果当前view没有能力处理,则与false情况一致,交由上级处理。
在View控件中是没有onInterceptTouchEvent,因为View控件已经没有下层的控件了,所以不需要拦截。主要方法的存在具体可看下表:

控件\方法dispatchTouchEventonInterceptTouchEventonTouchEvent
Activity存在不存在存在
ViewGroup存在存在存在
View存在不存在存在
二、方法返回值的影响实例

从onTouchEvent的返回值可以知道当返回super.onTouchEvent(ev)时,其结果与控件是否有能力处理有关系。因此以下案例可分成两种情况。

1.view有响应事件的能力,如Button:

工具类:

public class TouchEventUtil {
    /**
     * 获取事件的action
     * @param motionEvent
     * @return
     */
    public static String getTouchEventName(MotionEvent motionEvent){
        String touchEventName = "";
        switch (motionEvent.getAction()){
            case MotionEvent.ACTION_DOWN:
                touchEventName = "ACTION_DOWN";
                break;
            case MotionEvent.ACTION_UP:
                touchEventName = "ACTION_UP";
                break;
            case MotionEvent.ACTION_MOVE:
                touchEventName = "ACTION_MOVE";
                break;

        }
        return touchEventName;
    }
}

Activity代码:

public class TouchEventActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_touch_event);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("TouchEventActivity", "dispatchTouchEvent: TouchEventActivity====="+TouchEventUtil.getTouchEventName(ev));
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("TouchEventActivity", "onTouchEvent: TouchEventActivity====="+TouchEventUtil.getTouchEventName(event));
        return super.onTouchEvent(event);
    }
}

ViewGroup代码:

public class CustomViewGroup extends LinearLayout {


    public CustomViewGroup(Context context) {
        super(context);
    }

    public CustomViewGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomViewGroup(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CustomViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("TouchEventActivity", "dispatchTouchEvent: CustomViewGroup====="+TouchEventUtil.getTouchEventName(ev));
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("TouchEventActivity", "onInterceptTouchEvent: CustomViewGroup====="+TouchEventUtil.getTouchEventName(ev));
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("TouchEventActivity", "onTouchEvent: CustomViewGroup====="+TouchEventUtil.getTouchEventName(event));
        return super.onTouchEvent(event);
    }
}

Button的代码:

public class CustomButton extends androidx.appcompat.widget.AppCompatButton {

    public CustomButton(@NonNull Context context) {
        super(context);
    }

    public CustomButton(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomButton(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("TouchEventActivity", "dispatchTouchEvent: CustomButton====="+TouchEventUtil.getTouchEventName(event));
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("TouchEventActivity", "onTouchEvent: CustomButton====="+TouchEventUtil.getTouchEventName(event));
        return super.onTouchEvent(event);
    }
}

activity_touch_event布局代码:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TouchEventActivity"
    android:layout_gravity="center"
    android:gravity="center"
    android:background="@color/teal_700">
    <com.homework.stuproject.CustomViewGroup
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:background="@color/purple_200">
        <com.homework.stuproject.CustomButton
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="@color/purple_700">
        </com.homework.stuproject.CustomButton>
    </com.homework.stuproject.CustomViewGroup>

</LinearLayout>

具体样式如下:
在这里插入图片描述

a.假设控件返回均为默认值:
方法dispatchTouchEventonInterceptTounchEventonTouchEvent
Activitysuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)
CustomViewGroupsuper.dispatchTouchEvent(ev)super.onInterceptTouchEvent(ev)super.onTouchEvent(ev)
CustomButtonsuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)

点击中心区域,即点击CustomButton控件,打印结果如下:
在这里插入图片描述
从打印可以看出,因为CustomButton有响应事件的能力,此时super.onTouchEvent(ev)=true。

b.假设ViewGroup的onInterceptTouchEvent(ev)返回true,其余均为默认值:
方法dispatchTouchEventonInterceptTounchEventonTouchEvent
Activitysuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)
CustomViewGroupsuper.dispatchTouchEvent(ev)truesuper.onTouchEvent(ev)
CustomButtonsuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)

打印结果如下:
在这里插入图片描述
因为在CustomViewGroup中拦截了事件,因此事件不会再向下传递,只会调用CustomViewGroup的onTouchEvent(),但由于CustomViewGroup没有处理事件的能力,super.onTouchEvent(ev)结果为false,事件交由上层处理,即Activity中的onTouchEvent()。又因为Activity已经知道了CustomViewGroup没有处理事件的能力,因此ACTION_UP不会再向下传递,直接就被Activity调用onTouchEvent()处理。

c.假设ViewGroup的dispatchTouchEvent(ev)返回true,其余均为默认值:
方法dispatchTouchEventonInterceptTounchEventonTouchEvent
Activitysuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)
CustomViewGrouptruesuper.onInterceptTouchEvent(ev)super.onTouchEvent(ev)
CustomButtonsuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)

结果如下:
在这里插入图片描述

d.假设ViewGroup的dispatchTouchEvent(ev)返回false,其余均为默认值:
方法dispatchTouchEventonInterceptTounchEventonTouchEvent
Activitysuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)
CustomViewGroupfalsesuper.onInterceptTouchEvent(ev)super.onTouchEvent(ev)
CustomButtonsuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)

结果如下:
在这里插入图片描述
返回false,即事件不会向下传递,交由上层处理。

2.view没有响应事件的能力,如CustomView,代码如下:
public class CustomView extends View {


    public CustomView(Context context) {
        super(context);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CustomView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("TouchEventActivity", "dispatchTouchEvent: CustomView====="+TouchEventUtil.getTouchEventName(event));
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("TouchEventActivity", "onTouchEvent: CustomView====="+TouchEventUtil.getTouchEventName(event));
        return super.onTouchEvent(event);
    }
}

activity_touch_event布局代码,将原先的CustomButton修改为CustomView即可:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".TouchEventActivity"
    android:layout_gravity="center"
    android:gravity="center"
    android:background="@color/teal_700">
    <com.homework.stuproject.CustomViewGroup
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:layout_gravity="center"
        android:gravity="center"
        android:background="@color/purple_200">
        <com.homework.stuproject.CustomView
            android:layout_width="150dp"
            android:layout_height="150dp"
            android:background="@color/purple_700">
        </com.homework.stuproject.CustomButton>
    </com.homework.stuproject.CustomViewGroup>

</LinearLayout>
a.假设控件返回均为默认值:
方法dispatchTouchEventonInterceptTounchEventonTouchEvent
Activitysuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)
CustomViewGroupsuper.dispatchTouchEvent(ev)super.onInterceptTouchEvent(ev)super.onTouchEvent(ev)
CustomViewsuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)

点击中心区域,打印结果如下:
在这里插入图片描述
因为CustomView没有处理事件的能力,因此会一层一层向上执行onTouchEvent(),又因为ACTION_DOWN执行完之后Activity已经知道下级控件不能处理事件,因此ACTION_MOVE和ACTION_UP都不会向下传递,Activity直接调用onTouchEvent()处理。

b.假设控件CustomViewGroup的onInterceptTouchEvent返回true,其余返回均为默认值:
方法dispatchTouchEventonInterceptTounchEventonTouchEvent
Activitysuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)
CustomViewGrouptruesuper.onTouchEvent(ev)super.onTouchEvent(ev)
CustomViewsuper.dispatchTouchEvent(ev)super.onTouchEvent(ev)

代码运行结果如下:
在这里插入图片描述
事件在CustomViewGroup中被拦截,交由该控件的OnTouchEvent()响应该事件,再根据super.onTouchEvent(ev)返回false交由上层处理。

值得注意的是,对于没有响应事件能力的CustomView来说,只需要在不居中添加“android:clickable="true”就能响应事件了。
三、onTouch vs onTouchEvent vs onClick
1、执行顺序

onClick需要从ACTION_DOWN到ACTION_UP,所以onClick的执行顺序一定在onTouch和onTouchEvent之后,而onTouch又是执行在onTouchEvent之前,具体可看dispatchTouchEvent的源代码:

if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
            //noinspection SimplifiableIfStatement
            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;
            }
        }

如果view控件没有响应事件能力,在setOnTouchListener()方法之后,view空间就拥有响应事件能力,其实从上述的源码就可以知道,(mViewFlags & ENABLED_MASK) == ENABLED只会判断view控件的enabled值,如果在setOnTouchListener()之前调用了setEnabled(false),则onTouch不会被执行,且会根据view是否有能力响应事件去处理touch事件。但如果给view控件添加了点击监听setOnClickListener(),则控件就有了响应事件的能力,以下是setOnClickListener()的源码:

public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }
2、onTouch返回值的作用

return false:事件不被消费,onTouchEvent继续执行。
return true:事件被消费,onTouchEvent不会被执行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值