美文网首页Android自定义View
高级UI<第三十三篇>:事件分发机制的简单理解

高级UI<第三十三篇>:事件分发机制的简单理解

作者: NoBugException | 来源:发表于2020-01-19 23:42 被阅读0次

在Android开发中,我们经常会处理到屏幕触摸事件,而事件分发机制就是处理屏幕触摸事件的基础。事件分发机制其实很简单,网上有很多文章将事件分发机制讲解的很详细,但是就是因为太过详细导致可读性太低,导致读者理解起来非常的困难,从而使读者认为事件分发机制比较复杂。下面我将根据自己的理解,简单解读一下事件分发机制,保证没有一句废话。

(1)事件分发机制的流程图

图片.png

上图就是事件分发机制的流程图,该图就是本文的核心所在,本文主要目的是教您如何绘制上图,并在绘图的过程中真正了解事件分发机制的流程。

(2)实际场景

图片.png

假设,上图就是我们的实际场景,当点击图中的View时,最先触发的方法是什么?

(3)触摸事件的入口

当用户点击View时,Activity驱动会最先执行Activity的dispatchTouchEvent方法,也就是说,触摸事件的入口是:Activity的dispatchTouchEvent方法。

(4)责任链设计模式

Android的事件分发机制就好比责任链设计模式,将一个事件优先分发给Activity,当Activity不能处理该事件时将事件传递给ViewGroup,如果ViewGroup依然不能处理该事件,那么该事件就会被传递给View。

(5)事件分发机制重要的三个方法

图片.png

如图所示,Activity、ViewGroup、View共有的方法是:dispatchTouchEventonTouchEvent;其中ViewGroup多一个方法:onInterceptTouchEvent

这三个方法的职责分别是:

  • dispatchTouchEvent:主要职责是将事件发送出去;
  • onInterceptTouchEvent:是ViewGroup独有方法,我想这里唯一的目的是:将onInterceptTouchEvent的返回值设置为true(即拦截事件),并执行自身的onTouchEvent事件,事件将不会继续往下传递;
  • onTouchEvent:处理具体事件,如:MotionEvent.ACTION_DOWNMotionEvent.ACTION_MOVEMotionEvent.ACTION_CANCELMotionEvent.ACTION_UP

(6)学会画流程图

接下来,您需要学会绘制流程图,如果您不知道使用什么软件的话,那么我推荐大家使用ProcessOn完成流程图的绘制,ProcessOn官网地址如下:

https://www.processon.com

(7)写Demo,打印日志或者断点追踪

如果您想要知道事件分发的具体流程,我想方法有二:

【第一】 跟踪源码

源码追踪的方法虽好,但是太费事,个人感觉完全没必要;

【第二】 自定义View和自定义ViewGroup

首先准备一个重写dispatchTouchEventonTouchEvent方法的自定义View以及一个重写dispatchTouchEventonTouchEventonInterceptTouchEvent方法的自定义ViewGroup,点击View触发点击事件,根据日志或者断点追踪了解事件分发的流程;

布局如下:

<?xml version="1.0" encoding="utf-8"?>
<com.zyc.shapedemo.CustomLL
    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:orientation="vertical"
    tools:context=".MainActivity">

    <com.zyc.shapedemo.MyButton
        android:id="@+id/button_1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:text="按钮"/>


</com.zyc.shapedemo.CustomLL>

CustomLL其实就是一个LinearLayout,如下:

public class CustomLL extends LinearLayout {

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

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d("yunchong", "CustomLL -- dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d("yunchong", "CustomLL -- onInterceptTouchEvent");
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("yunchong", "CustomLL -- onTouchEvent");
        return super.onTouchEvent(event);
    }
}

MyButton是一个按钮,代码如下:

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


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

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

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

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d("yunchong", "MyButton -- dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.d("yunchong", "MyButton -- onTouchEvent");
        return super.onTouchEvent(event);
    }
}

被重写的三个方法都有三种返回值,分别是:super、true、false。

  • super:返回super一般都是将事件分发给下级;(但,有个别设备的问题需要注意,下文再说),如果没有下级,则结束分发;
  • true:dispatchTouchEvent和onTouchEvent方法直接结束分发,onInterceptTouchEvent则将事件交给自身的onTouchEvent方法处理;
  • false:dispatchTouchEvent和onTouchEvent方法返回false的话直接将事件交给上一级的onTouchEvent方法,onInterceptTouchEvent返回false的话将事件分发给下级的dispatchTouchEvent方法;

利用以上demo,并且根据日志或者断点一点一点地追踪事件分发执行的流程,最终绘制事件分发机制的流程,绘制出来的效果图在文章开始已经贴出。

(8)特殊情况

文章开头贴出的事件分发流程图是只有一个ViewGroup的情况,那么如果是以下布局的情况呢?

<?xml version="1.0" encoding="utf-8"?>
<com.zyc.shapedemo.CustomLL
    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:orientation="vertical"
    tools:context=".MainActivity">

    <com.zyc.shapedemo.CustomLL2
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.zyc.shapedemo.MyButton
            android:id="@+id/button_1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:textSize="20sp"
            android:text="按钮"/>

    </com.zyc.shapedemo.CustomLL2>


</com.zyc.shapedemo.CustomLL>

也就是说,View被双重ViewGroup包裹,其流程图如下:

事件分发机制2.jpg

如图所示,一般情况下,不管嵌套了多少个ViewGroup,其分发流程是一样的,图中红色线条是个别设备的bug,将super换成false即可解决。

[本章完...]

相关文章

网友评论

    本文标题:高级UI<第三十三篇>:事件分发机制的简单理解

    本文链接:https://www.haomeiwen.com/subject/dtxhzctx.html