Android一文让你轻松搞定Touch事件分发,小程序FMP优化实录

思路梳理


在深入分析事件分发源码之前,需要先弄清楚2个概念。

ViewGroup

ViewGroup 是一组 View 的组合,在其内部有可能包含多个子 View,当手指触摸屏幕上时,手指所在的区域既能在ViewGroup显示范围内,也可能在其内部 View 控件上。因此它内部的事件分发的重心是处理当前 ViewGroup 和子 View 之间的逻辑关系:

  • 1.当前 ViewGroup 是否需要拦截Touch事件;

  • 2.是否需要将Touch事件继续分发给子 View;

  • 3.如何将Touch事件分发给子 View。

View

View 是一个单纯的控件,不能再被细分,内部也并不会存在子 View,所以它的事件分发的重点在于当前 View 如何去处理 Touch 事件,并根据相应的手势逻辑进行一些列的效果展示(比如滑动,放大,点击,长按等)。

  • 1.是否存在 TouchListener;

  • 2.是否自己接收处理 touch 事件(主要逻辑在 onTouchEvent 方法中)。

涉及事件分发的方法


方法的简单用途解析


我们可以发现这三个方法的返回值都为boolean类型,其实它们就是通过返回值来决定下一步的传递处理方向。

1、dispatchTouchEvent() ——用来分发事件所用

该方法会将Touch事件**「自上而下」依次分发到子元素中,直到被终止或者到达View层,该方法也是采用一种「隧道方式来分发」。在其中会调用onInterceptTouchEvent()和onTouchEvent(),「一般不会重写」**。

  • 返回false则不拦截继续往下分发;

  • 返回true则拦截住该事件不在向下层元素分发;

在dispatchTouchEvent()方法中默认返回false。

2、onInterceptTouchEvent() ——用来拦截事件所用

  • 返回false不拦截事件,Touch事件就会往下传递给其子View。

  • 返回true,该事件将会被拦截,并且被当前ViewGroup处理,调用ViewGroup的onTouchEvent()方法。

3、onTouchEvent() ——用来处理事件

  • 返回true则表示该View能处理该事件,事件将终止向上传递(传递给其父View)。

  • 返回false表示不能处理,则把事件传递给其父View的onTouchEvent()方法来处理。

拥有上述方法的类


「注意:」 需要特别注意一点就是ViewGroup中额外拥有onInterceptTouchEvent()方法,其他两个方法为这三种类所共同拥有。

事件分发流程


单个事件触发后,事件分发流程:Activity①>ViewGroup②>View③,如下图:

即要想充分理解Android分发机制,本质上是要理解:

  • Activity对点击事件的分发机制

  • ViewGroup对点击事件的分发机制

  • View对点击事件的分发机制

从U型图中可以发现,由父组件不断向子组件分发,若子组件能够处理,则立刻返回。若子组件都不处理,那传递到底层的子组件,再返回回来。「整个View之间的事件分发,实质上就是一个大的递归函数」

实例

==

下面咱们写一个简单实例来更好的理解这个大U型图,效果图:

创建实例


创建MyViewGroup继承ViewGroup

重写dispatchTouchEvent()、onInterceptTouchEvent()、onTouchEvent()方法

public class MyViewGroup extends RelativeLayout {

public MyViewGroup(Context context) {

super(context);

}

public MyViewGroup(Context context, AttributeSet attrs) {

super(context, attrs);

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

MLog.logEvent(“MyViewGroup.dispatchTouchEvent:”,ev);

return super.dispatchTouchEvent(ev);

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

MLog.logEvent(“MyViewGroup.onInterceptTouchEvent:”,ev);

return false;

}

@Override

public boolean onTouchEvent(MotionEvent event) {

MLog.logEvent(“MyViewGroup.onTouchEvent自行处理:”,event);

return super.onTouchEvent(event);

}

}

创建MyView继承View

重写dispatchTouchEvent()、onTouchEvent()方法

public class MyView extends View {

public MyView(Context context) {

super(context);

}

public MyView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

MLog.logEvent(“MyView.dispatchTouchEvent:”,ev);

return super.dispatchTouchEvent(ev);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

MLog.logEvent(“MyView.onTouchEvent:”,event);

return super.onTouchEvent(event);

}

}

创建TouchActivity继承Activity

重写dispatchTouchEvent()、onTouchEvent()方法

public class TouchActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_touch);

}

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

MLog.logEvent(“TouchActivity.dispatchTouchEvent:”,ev);

return super.dispatchTouchEvent(ev);

}

@Override

public boolean onTouchEvent(MotionEvent event) {

MLog.logEvent(“TouchActivity.onTouchEvent:”,event);

return super.onTouchEvent(event);

}

}

创建布局文件

添加控件MyViewGroup和MyView

<?xml version="1.0" encoding="utf-8"?>

<com.scc.demo.view.MyViewGroup xmlns:android=“http://schemas.android.com/apk/res/android”

android:id=“@+id/mtrg_touch”

android:layout_width=“match_parent”

android:layout_height=“300dp”

android:background=“@color/color_FF773D”>

<com.scc.demo.view.MyView

android:id=“@+id/mtv_onclick”

android:layout_width=“120dp”

android:layout_height=“60dp”

android:layout_centerHorizontal=“true”

android:layout_marginTop=“40dp”

android:background=“@color/color_188FFF”/>

</com.scc.demo.view.MyViewGroup>

MLog.logEvent()

方便查看对应事件

public static void logEvent(String msg, MotionEvent event) {

String motionEvent = “”;

switch (event.getAction()){

case MotionEvent.ACTION_DOWN://在屏幕按下时(所有事件的开始)

motionEvent=“DOWN”;

break;

case MotionEvent.ACTION_UP://在屏幕抬起时(与DOWN对应)

motionEvent=“UP”;

break;

case MotionEvent.ACTION_MOVE://在屏幕上滑动时

motionEvent=“MOVE”;

break;

case MotionEvent.ACTION_CANCEL://滑动超出控件边界时

motionEvent=“CANCEL”;

break;

}

Log.e(“SccEvent”, msg + motionEvent);

}

点击页面,看效果


点击Activity(白色区域)

运行结果

E/SccEvent: TouchActivity.dispatchTouchEvent:DOWN

E/SccEvent: TouchActivity.onTouchEvent:DOWN

E/SccEvent: TouchActivity.dispatchTouchEvent:MOVE

E/SccEvent: TouchActivity.onTouchEvent:MOVE

E/SccEvent: TouchActivity.dispatchTouchEvent:MOVE

E/SccEvent: TouchActivity.onTouchEvent:MOVE

E/SccEvent: TouchActivity.dispatchTouchEvent:UP

E/SccEvent: TouchActivity.onTouchEvent:UP

点击ViewGroup(黄色区域)

运行结果

E/SccEvent: TouchActivity.dispatchTouchEvent:DOWN

E/SccEvent: MyViewGroup.dispatchTouchEvent:DOWN

E/SccEvent: MyViewGroup.onInterceptTouchEvent:DOWN

E/SccEvent: MyViewGroup.onTouchEvent:DOWN

E/SccEvent: TouchActivity.onTouchEvent:DOWN

E/SccEvent: TouchActivity.dispatchTouchEvent:MOVE

E/SccEvent: TouchActivity.onTouchEvent:MOVE

E/SccEvent: TouchActivity.dispatchTouchEvent:MOVE

E/SccEvent: TouchActivity.onTouchEvent:MOVE

E/SccEvent: TouchActivity.dispatchTouchEvent:UP

E/SccEvent: TouchActivity.onTouchEvent:UP

点击View(蓝色区域)

运行结果

E/SccEvent: TouchActivity.dispatchTouchEvent:DOWN

E/SccEvent: MyViewGroup.dispatchTouchEvent:DOWN

E/SccEvent: MyViewGroup.onInterceptTouchEvent:DOWN

E/SccEvent: MyView.dispatchTouchEvent:DOWN

E/SccEvent: MyView.onTouchEvent:DOWN

E/SccEvent: MyViewGroup.onTouchEvent:DOWN

E/SccEvent: TouchActivity.onTouchEvent:DOWN

E/SccEvent: TouchActivity.dispatchTouchEvent:MOVE

E/SccEvent: TouchActivity.onTouchEvent:MOVE

E/SccEvent: TouchActivity.dispatchTouchEvent:MOVE

E/SccEvent: TouchActivity.onTouchEvent:MOVE

E/SccEvent: TouchActivity.dispatchTouchEvent:UP

E/SccEvent: TouchActivity.onTouchEvent:UP

结果分析

重叠区域越多触发事件越多。这就用到了事件分发。点击蓝色区域,走完了一个大U分发,没有人拦截和处理。

看看上面的日志你会发现按下事件走了那么多方法,为什么滑动和抬起仅调用Activity中的方法?

原因就是:MOVE、UP 等事件的分发交给谁,取决于它们的起始事件 Down 是由谁捕获的。可参阅文章后面的**「为什么 DOWN 事件特殊」**

本次将所有Touch事件打印,后续仅打印DOWN事件。

事件分发和处理


Activity处理和分发

Activity处理

1.将MyViewGroup.dispatchTouchEvent()返回修改为false

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

MLog.logEvent(“MyViewGroup.dispatchTouchEvent:”,ev);

return false;

}

运行结果

E/SccEvent: TouchActivity.dispatchTouchEvent:DOWN

E/SccEvent: MyViewGroup.dispatchTouchEvent:DOWN

E/SccEvent: TouchActivity.onTouchEvent:DOWN

E/SccEvent: TouchActivity.SccEvent就在这里玩

2.MyViewGroup仅拦截不处理,也会交给Activity.onTouchEvent处理

3.向下分发的事件ViewGroup和View都不处理,最后还是交给Activity.onTouchEvent处理(如**「上面点击蓝色区域结果分析」**)

Activity分发

Actvitiy是事件分发的顶层,dispatchTouchEvent方法返回true不做分发,事件结束。返回false或者super.dispatchTouchEvent(ev)则向下分发事件。向下分发就没必要处理(重写)dispatchTouchEvent()

ViewGroup拦截处理和分发

ViewGroup拦截处理

确保Activity做好分发后(即不对dispatchTouchEvent做修改),「修改MyViewGroup.onInterceptTouchEvent()和MyViewGroup.onTouchEvent()方法」,ViewGroup的重点在于事件拦截(onInterceptTouchEvent),所以咱重写拦截事件和处理事件的两个方法即可。

@Override

public boolean dispatchTouchEvent(MotionEvent ev) {

MLog.logEvent(“MyViewGroup.dispatchTouchEvent:”,ev);

return super.dispatchTouchEvent(ev);

}

@Override

public boolean onInterceptTouchEvent(MotionEvent ev) {

MLog.logEvent(“MyViewGroup.onInterceptTouchEvent:”,ev);

return true;

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

写在最后

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从哪里入手去学习,对此我整理了一些资料

如果你熟练掌握以下列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包,最后觉得有帮助、有需要的朋友可以点个赞

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

写在最后

很多人在刚接触这个行业的时候或者是在遇到瓶颈期的时候,总会遇到一些问题,比如学了一段时间感觉没有方向感,不知道该从哪里入手去学习,对此我整理了一些资料

如果你熟练掌握以下列出的知识点,相信将会大大增加你通过前两轮技术面试的几率!这些内容都供大家参考,互相学习。

①「Android面试真题解析大全」PDF完整高清版+②「Android面试知识体系」学习思维导图压缩包,最后觉得有帮助、有需要的朋友可以点个赞

[外链图片转存中…(img-GRqfg6uP-1712375407255)]

[外链图片转存中…(img-t6j32Z0P-1712375407255)]

[外链图片转存中…(img-3876ssms-1712375407255)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值