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

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

假设,上图就是我们的实际场景,当点击图中的View时,最先触发的方法是什么?
(3)触摸事件的入口
当用户点击View时,Activity驱动会最先执行Activity的dispatchTouchEvent
方法,也就是说,触摸事件的入口是:Activity的dispatchTouchEvent
方法。
(4)责任链设计模式
Android的事件分发机制就好比责任链设计模式,将一个事件优先分发给Activity,当Activity不能处理该事件时将事件传递给ViewGroup,如果ViewGroup依然不能处理该事件,那么该事件就会被传递给View。
(5)事件分发机制重要的三个方法

如图所示,Activity、ViewGroup、View共有的方法是:dispatchTouchEvent
、onTouchEvent
;其中ViewGroup多一个方法:onInterceptTouchEvent
这三个方法的职责分别是:
- dispatchTouchEvent:主要职责是将事件发送出去;
- onInterceptTouchEvent:是ViewGroup独有方法,我想这里唯一的目的是:将
onInterceptTouchEvent
的返回值设置为true(即拦截事件),并执行自身的onTouchEvent
事件,事件将不会继续往下传递; - onTouchEvent:处理具体事件,如:
MotionEvent.ACTION_DOWN
、MotionEvent.ACTION_MOVE
、MotionEvent.ACTION_CANCEL
、MotionEvent.ACTION_UP
;
(6)学会画流程图
接下来,您需要学会绘制流程图,如果您不知道使用什么软件的话,那么我推荐大家使用ProcessOn完成流程图的绘制,ProcessOn官网地址如下:
(7)写Demo,打印日志或者断点追踪
如果您想要知道事件分发的具体流程,我想方法有二:
【第一】
跟踪源码
源码追踪的方法虽好,但是太费事,个人感觉完全没必要;
【第二】
自定义View和自定义ViewGroup
首先准备一个重写dispatchTouchEvent
和onTouchEvent
方法的自定义View以及一个重写dispatchTouchEvent
、onTouchEvent
、onInterceptTouchEvent
方法的自定义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包裹,其流程图如下:

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