Android中事件分发机制的理解

本文通过一个示例详细解析了Android中事件分发机制的工作原理,包括dispatchTouchEvent、onInterceptTouchEvent及onTouchEvent三个关键方法的作用及调用顺序。

之前学一个新的知识,在学的时候总感觉自己会了,但是当用的时候还是懵B,然后继续去找博客学习,然后想,这也不是个事吧,既然学的时候感觉自己会了,为什么不在当时记录下来呢,就算过后自己忘记了,翻翻自己的博客,根据自己原来的理解去过一遍,那样不是比去看别人的博客更加容易重拾知识吗,而且将学习心得记录下来本身也是对知识点的一种巩固,然后。。。就有了这篇文章

这篇文章并没有涉及到源码层次,只是自己在学习的过程中总结的一些经验,所以如果你对android中的事件分发机制还有些许疑惑的时候可以来看看本篇文章,希望对您有所帮助

首先,我们需要知道在事件机制中有三个方法:
(1)dispatchTouchEvent 对touch事件进行分发的方法
(2)onInterceptTouchEvent 对touch事件进行拦截的方法
(3)onTouchEvent 对touch事件进行处理的方法

下面我们来用一个Demo来跟踪一下这三个方法的调用顺序

布局代码如下所示

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    android:id="@+id/activity_main"
    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.touchdemo.MainActivity">

    <com.touchdemo.ViewGroupA
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <com.touchdemo.ViewGroupB
            android:background="@android:color/holo_blue_dark"
            android:layout_width="300dp"
            android:layout_height="300dp">
            <com.touchdemo.ViewC
                android:background="@android:color/holo_green_dark"
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:id="@+id/click"
                />
        </com.touchdemo.ViewGroupB>
    </com.touchdemo.ViewGroupA>
</LinearLayout>

布局图
这里写图片描述

我们在ViewGroupA、ViewGroupB、ViewC中都重写了dispatchTouchEvent()、onTouchEvent()方法,在ViewGroupA、ViewGroupB中还重写了onInterceptTouchEvent()方法,因为View中是没有onInterceptTouchEvent()方法的所以ViewC中是没有该方法的,并且在每个重写的方法中添加Log打印的代码片段
ViewGroupA和ViewGroupB的代码基本一样,如下

public class ViewGroupA extends LinearLayout {
    String TAG = "ViewGroupA";

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

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e(TAG, "dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e(TAG, "onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "onTouchEvent");
        return super.onTouchEvent(event);
    }
}

ViewC的代码如下:

public class ViewC extends TextView {
    String TAG = "ViewC";

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

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

    public ViewC(Context context, AttributeSet attrs, int defStyleAttr) {

        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.e(TAG, "dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "onTouchEvent");
       return super.onTouchEvent(event);
    }
}

我们在不做任何处理的情况下运行代码,输出Log如下
这里写图片描述
来张配图
这里写图片描述
从图中我们可以知道:事件派发顺序是从上到下,事件的消费则是从下往上。

可能很多人看到这不明白了,什么事件派发,什么事件消费?呃呃….
还是用代码说话,我们在ViewGroupA的dispatchTouchEvent方法中返回true

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e(TAG, "dispatchTouchEvent");
return true;
}

运行代码,Log如下

E/ViewGroupA: dispatchTouchEvent

这里写图片描述

是的,点击屏幕后就调用了dispatchTouchEvent方法,后面的方法完全没有反应,这说明在dispatchTouchEvent方法中返回true后,直接将事件给掐断(找不到什么好的词来形容),事件到此为止

然后将代码还原后,我们在ViewGroupA的onInterceptTouchEvent方法中返回true

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.e(TAG, "dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.e(TAG, "onInterceptTouchEvent");
        return true;
    }

运行代码,Log如下

E/ViewGroupA: dispatchTouchEvent
E/ViewGroupA: onInterceptTouchEvent
E/ViewGroupA: onTouchEvent

这里写图片描述

看到这里有些人也许明白了,dispatchTouchEvent方法是掐断事件,而onInterceptTouchEvent方法只是将事件拦截下来传到了自己的onTouchEvent方法中

我们再将代码还原,然后再ViewC的onTouchEvent方法中反回true

@Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.e(TAG, "onTouchEvent");
        return true;
    }

运行代码,Log如下
E/ViewGroupA: dispatchTouchEvent
E/ViewGroupA: onInterceptTouchEvent
E/ViewGroupB: dispatchTouchEvent
E/ViewGroupB: onInterceptTouchEvent
E/ViewC: dispatchTouchEvent
E/ViewC: onTouchEvent

这里写图片描述

onTouchEvent方法代表事件的消费,如果我们返回true则表示我们需要消费此事件,当然就不会再往上传递啦。

从上面几个代码的实现效果来看,如果我们在任何方法中返回了true,那么事件将不会再向下一级传递,只是在onInterceptTouchEvent方法中返回true会让事件向自己或者像自己的父级传递。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值