Android交互系列(一)之事件分发机制

前言

在智能手机普及之前,人们依靠键盘与手机交互,可是实现的功能很有限,使用起来也很不方便。智能手机出现之后,人与手机的交互变得多样,只需要点一点,滑一滑就能实现很多功能。尤其现在手机的发展的趋势是连仅存虚拟键盘都要去除,完全凭借手势去操控手机,比如现在很多手机向左滑就是退出页面,现在想像一个场景,就是你看小说翻页也需要向左滑,如果你向左滑就退出了,这肯定不是你想要的,那么怎么实现在看小说的时候不退出呢?这就引出了我们今天的主题——事件分发,希望你看完这篇文章就会得到你心中的答案。

目录

事件的分类

事件是事件分发的对象,也就是与屏幕的交互,事件有以下四种

注:就一般而言,move事件在一次滑动中会多次触发,比如在B点按下,滑动到C点,然后离开,在滑动期间会发生多次而不是一次move事件。

相关函数

在讨论相关函数之前,我们先看一下事件是在谁之间传递的。一般App的结构可能如下图所示,一个Activity,Activity中含有ViewGroup,ViewGroup中包含着View。事件就是在Activity,ViewGroup和View之间传递。

相关函数如下图

其中dispatchTouchEvent是当事件传入的时候调用,用于分发事件。OnTouchEvent处理事件,OnInterceptTouchEvent是ViewGroup特有事件,是否拦截事件,如果拦截事件则事件不会接着流向view。

事件分发的过程

仅仅通过上面三个函数不能很直观的了解这个过程,接下来我们用一个例子来说明这个过程。例子也很简单,一个Activity,里面有一个LinearLayout作为ViewGroup,ViewGroup里面是一个WebView,布局文件如下,因为要重写三个函数,所以LinearLayout 和WebView都重写了就是其中的LL和TWebView。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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=".MainActivity">
    <io.appetizer.touch.LL
        android:id="@+id/ll"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    <io.appetizer.touch.TWebView
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </io.appetizer.touch.TWebView>
    </io.appetizer.touch.LL>
</androidx.constraintlayout.widget.ConstraintLayout>

在什么都不处理的情况下,事件传递的情况如下

从这张图我们可以发现两件事:

(1)事件的传递顺序是 Activity -> ViewGroup -> View,在没有拦截的情况下事件处理的顺序是 View -> ViewGroup -> Activity。

(2)如我们把从ACTION_DOWN,ACTION_MOVE, ACTION_UP看作一个整体,当ACTION_DOWN传递下去,结果没有响应,没有被消费,又返回到Activity的onTouchEvent,则后续事件不会再向下传递。

实验一

注意:三个函数的返回值都是布尔值

当我们让ViewGroup的OnInterceptTouchEvent返回false,结果如下

当我们让ViewGroup的OnInterceptTouchEvent返回true,结果如下

可以看到当OnInterceptTouchEvent返回true时,ViewGroup会拦截事件,事件不会再流向View。

实验二

从实验一我们可以看出ViewGroup拦截了事件,但是ViewGroup并没有消费事件,事件最后还是流回了Activity,那么我们如何在ViewGroup消费事件,并且不想Activity对事件作出反应,否则可能会导致滑动冲突,就如我们开篇所讲的翻页和退出App之间的冲突,如果事件流回了Activity,那么可能直接退出了,这不是我们所想要的。为了消费事件,一般要对控件添加setOnTouchListener的监听,并且返回true,表示事件就此消费,不再往回传递,代码如下:

   LinearLayout ll = findViewById(R.id.ll);
        ll.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                Log.d("TEST", "ViewGroup 消费事件" );
                return true;
            }
        });

执行结果如下:

从图中我们可以看到事件在ViewGroup被消费,并且没有再回传给Activity。

实验三

实验二我们看到了GroupView如何拦截和消费事件,那么View又是如何消费事件的呢,大部份的流程是一样的,都需要设置setOnTouchListener,返回true,还需要加上一个函数requestDisallowInterceptTouchEvent(boolean disallowIntercept)请求父控件不要拦截事件,参数为ture,则不会拦截,代码如下

   TWebView tWebView = findViewById(R.id.tWebView);
       tWebView.setOnTouchListener(new View.OnTouchListener() {
           @Override
           public boolean onTouch(View v, MotionEvent event) {
               v.getParent().requestDisallowInterceptTouchEvent(true);
               Log.d("TEST TOUCH", "View 消费事件");
               return true;
           }
       });

执行结果如下

如图所示,事件被View消费且没有回传。

最后

如有错误,欢迎指正,如果觉得写得还行,点赞就是最大的支持。更多资料和文章可以关注微信公众号QStack,追寻最纯粹的技术,享受编程的快乐。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值