Android事件传递机制

本文详细解析了Android中事件分发、拦截及消费的过程,通过具体的代码示例和自定义View组展示dispatchTouchEvent(), onInterceptTouchEvent(), onTouchEvent()等方法的工作原理。

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

本人作为一个技术小菜鸟,第一次写博客,有写的不好的地方希望大家多多指教,本文章一来给大家做个参考,二来说不定哪天忘记了回来看看也是好的。哈哈,废话不多说,直接进入主题!

事件传递所涉及到的方法

  1. dispatchTouchEvent() 分发方法
  2. onInterceptTouchEvent() 拦截方法
  3. onTouchEvent() 消费方法

首先简单介绍下这三个方法 :
dispatchTouchEvent() 这个方法代表事件分发,作用是改变事件传递的方向。
onInterceptTouchEvent()对事件进行拦截,作用拦截事件交给当前处理或传递到下一级。
onTouchEvent()主要的作用是消费MotionEvent事件。


方法ActivityViewGroupView
dispatchTouchEvent()
onInterceptTouchEvent()
onTouchEvent()

Activity和View中是没有事件拦截方法,只有分发和消费方法,具体Why这个要问谷歌了! 哈哈 这个不做解释 ..
首先我们看一下三个方法 返回的是super.XX() 还有两种返回值true/false .

  • 分发
    返回true时代表事件不分发,事件到此结束。
    返回false时,会把事件传递给上一级的onTouchEvent()处理。
    返回super.dispatchTouchEvent(ev)时分两种情况,一种是ViewGroup 会把事件传递给当前的onInterceptTouchEvent()方法处理,另一种是View 直接交给当前的onTouchEvent()事件处理。
  • 拦截
    返回true时,会把事件传递给当前的onTouchEvent()处理。
    返回false/super.onInterceptTouchEvent(ev) 这个时候代表事件不拦截,事件会传向下一级的dispatchTouchEvent()。
  • 消费
    返回true时代表此次是事件已消费,并且以后的事件如果中途没被拦截的话会一直走到当前方向
    返回false/super.onTouchEvent(ev) 这个时候代表事件不拦截,事件会传向上一级的onTouchEvent()。
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);//true/false
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);//true/false
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);//true/false
    }

动手做个demo

a

首先准备一个Activity
创建三个自定义View(其实两个就可以) FatherA,FatherB,ChildC

CustomViewActivity

public class CustomViewActivity extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {
        super.onCreate(savedInstanceState, persistentState);
        setContentView(R.layout.activity_custom);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }
}

FatherA

public class FatherA extends LinearLayout {
    public FatherA(Context context) {
        super(context);
    }

    public FatherA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        return super.dispatchTouchEvent(ev);//true
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev);//true
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }
}

FatherB和FatherA一样 此处省略
ChildC

public class ChildC extends View {
    public ChildC(Context context) {
        super(context);
    }

    public ChildC(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        return super.onTouchEvent(event);
    }
}

最后贴一个主布局的xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:gravity="center"
              android:orientation="vertical">

    <com.sincerly.android.activity.customView.view.FatherA
        android:layout_width="300dp"
        android:layout_height="300dp"
        android:gravity="center"
        android:background="@color/blue">

        <com.sincerly.android.activity.customView.view.FatherB
            android:layout_width="200dp"
            android:background="@color/green"
            android:gravity="center"
            android:layout_height="200dp">

            <com.sincerly.android.activity.customView.view.ChildC
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:background="@color/white"/>
        </com.sincerly.android.activity.customView.view.FatherB>
    </com.sincerly.android.activity.customView.view.FatherA>
</LinearLayout>

测试一,点击FatherB(绿色区域),FatherA的分发方法返回true,其他都为默认


E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: FatherA DispatchTouchEvent()

E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: FatherA DispatchTouchEvent()

可以根据Log看出FatherB这个View根本没获取到事件 因为FatherA的分发方法返回了true,就好像给事件加了个标示 如果到FatherA的分发方法事件就无效了。

下面我们把FatherA分发事件改为false

E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: FatherA DispatchTouchEvent()
E/tag: CustomViewActivity onTouchEvent()

E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: CustomViewActivity onTouchEvent()

分发方法返回false把事件进行向上传递,给上级的onTouchEvent()处理。

下面我们把FatherA分发事件改为super.dispatchTouchEvent(ev)



E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: FatherA DispatchTouchEvent()
E/tag: FatherA onInterceptTouchEvent()
E/tag: FatherB DispatchTouchEvent()
E/tag: FatherB onInterceptTouchEvent()
E/tag: FatherB onTouchEvent()
E/tag: FatherA onTouchEvent()
E/tag: CustomViewActivity onTouchEvent()

E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: CustomViewActivity onTouchEvent()

可见分发事件只有返回super.dispatchTouchEvent(ev)才会进行分发。
这里解释一下,点击的是绿色 也就是FatherB,处理流程,FatherA分发返回super.d() 进行传递至FatherA的拦截方法 拦截方法返回的super.onInterceptTouchEvent() 不进行拦截 并将事件传递给FatherB的分发事件 然后同理 FatherB 进行分发 ,不拦截(因为我们点击的是绿色区域 并没有点击到ChildC(内层白色区域) ) 所以不拦截之后, 交给当前(FatherB)的onTouchEvent()处理 因为返回的false,所以事件向上传递给FatherA的onTouchEvent() 因为返回的false 所以向上传递给Activity的onTouchEvent()处理.


方法ActivityFatherAFatherBChildC
dispatchTouchEvent()super.d()super.d()super.d()super.d()
onInterceptTouchEvent()super.onI()/falsesuper.onI()/false
onTouchEvent()super.on()super.on()super.on()super.on()

拦截方法和消费方法 返回super和false的区别 有兴趣可以自行测试,因为效果一样,我这里不做过多解释了。

下面我们测试一下FatherA的拦截方法,返回true

E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: FatherA DispatchTouchEvent()
E/tag: FatherA onInterceptTouchEvent()
E/tag: FatherA onTouchEvent()
E/tag: CustomViewActivity onTouchEvent()

E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: CustomViewActivity onTouchEvent()

可见虽然点击了FatherB ,但是在A处 事件已经拦截。
下图为当前事件的返回值,A拦截返回true,然后走A的onTouchEvent() 返回false,事件并没有被消费, 所以返回Activity的onTouchEvent() 然后整个事件处理完毕。

记得之前有一个例子不错 我引用一下。
校长 教师 班长 小明
校长有一张表不想写,就把这张表下发给教师 说:”你帮我填一下”,然后这个老师也比较懒,然后给班长, 班长有急事然后交给小明说 “帮忙填一下”,小明不会写 ,然后给班长说:”我不会写”, 班长给老师说“我有事 写不了”,然后老师给校长说我不想填(哈哈,找抽),最终表还是到了校长的手中。

如果教师一开始就说“没问题”, 这就是拦截方法 为true 并且如果表填完了,onTouchEvent()方法就为true 也就是说这个表已经填好了, 而且下次再来一张表的时候 还是到这个地方结束。


方法ActivityFatherAFatherBChildC
dispatchTouchEvent()super.d()super.d()super.d()super.d()
onInterceptTouchEvent()truesuper.onI()/false
onTouchEvent()super.on()super.on()super.on()super.on()

再做最后一个测试 就是FatherB的拦截返回true,消费返回true
其他全为super.();


E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: FatherA DispatchTouchEvent()
E/tag: FatherA onInterceptTouchEvent()
E/tag: FatherB DispatchTouchEvent()
E/tag: FatherB onInterceptTouchEvent()
E/tag: FatherB onTouchEvent()

E/tag: CustomViewActivity DispatchTouchEvent()
E/tag: FatherA DispatchTouchEvent()
E/tag: FatherA onInterceptTouchEvent()
E/tag: FatherB DispatchTouchEvent()
E/tag: FatherB onTouchEvent()

可以看出流程,DOWN事件 进入Activity分发,A分发,A不拦截,到B的 分发,拦截,消费。


由于第一次写博文,经验非常不足,有的解释的没到点上 请谅解,希望对大家有点帮助,缺个流程图,会直观点 ,正在积极制作中。 有没有会做动态图的 那个是什么软件做的? 就是效果演示的那个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值