java 事件分发机制_读Android源码之事件分发机制最全总结

本文详细介绍了Android中事件分发机制,从Touch事件的生命周期开始,涵盖View、ViewGroup和Activity的事件处理方法,包括dispatchTouchEvent、onTouchEvent和onInterceptTouchEvent。文章通过实例解析了事件从底层硬件到上层应用的传递过程,并总结了事件分发的流程图,强调了事件拦截和消费的关键点。
摘要由CSDN通过智能技术生成

原标题:读Android源码之事件分发机制最全总结

本文源码来自andorid sdk 22,不同版本会有细微差别,但核心机制是一致的

一.概述

事件分发有多种类型, 本文主要介绍Touch相关的事件分发.

整个事件分发流程中,会有大量MotionEvent对象,该对象用于记录所有与移动相关的事件,比如手指触摸屏幕事件。

一次完整的MotionEvent事件,是从用户触摸屏幕到离开屏幕。整个过程的动作序列:ACTION_DOWN(1次) ->ACTION_MOVE(N次) ->ACTION_UP(1次),

多点触摸,每一个触摸点Pointer会有一个id和index。对于多指操作,通过pointerindex来获取指定Pointer的触屏位置。比如,对于单点操作时获取x坐标通过getX(),而多点操作获取x坐标通过getX(pointerindex)

对于View,ViewGroup,Activity都能处理Touch事件, 它们之间处理的先后顺序和方法有所不同.

1.1 View

View是所有视图对象的父类,实现了动画相关的接口Drawable.Callback, 按键相关的接口KeyEvent.Callback, 交互相关的接口AccessibilityEventSource。比如Button继承自View。

TouchEvent事件处理相关的方法:

dispatchTouchEvent(MotionEvent event)

onTouchEvent(MotionEvent event)

1.2 ViewGroup

ViewGroup,是一个abstract类,一组View的集合,可以包含View和ViewGroup,是所有布局的父类或间接父类。继承了View,实现了ViewParent(用于与父视图交互的接口),ViewManager(用于添加、删除、更新子视图到Activity的接口)。比如常用的LinearLayout,RelativeLayout都是继承自ViewGroup。

TouchEvent事件处理相关的方法:

dispatchTouchEvent(MotionEvent event)

onInterceptTouchEvent(MotionEvent ev)

onTouchEvent(MotionEvent event)

1.3 Activity

Activity是Android四大基本组件之一,当手指触摸到屏幕时,屏幕硬件一行行不断地扫描每个像素点,获取到触摸事件后,从底层产生中断上报。再通过native层调用Java层InputEventReceiver中的dispatchInputEvent方法。经过层层调用,交由Activity的dispatchTouchEvent方法来处理。

TouchEvent事件处理相关的方法:

dispatchTouchEvent(MotionEvent event)

onTouchEvent(MotionEvent event)

二. 分发原理

文章的小节[3.3]已介绍事件分发过程的前期工作. 当UI主线程收到底层上报的input事件,便会调用InputEventReceiver.dispachInputEvent方法.

795b23bcb76005dc92a8a8751e7e9fa8.png

2.1 DecorView.dispatchTouchEvent

[-> PhoneWindow.java ::DecorView]

ff54a4acf9e1ca03a8c645d66a337db8.png

此处cb是指Window的内部接口Callback. 对于Activity实现了Window.Callback接口. 故接下来调用Activity类.

2.2 Activity.dispatchTouchEvent

[-> Activity.java]

df02821a497c0511c1b70012059f2caf.png

如果重写Activity的该方法,则会在分发事件之前拦截所有的触摸事件. 另外此处getWindow()返回的是Activity的mWindow成员变量, 该变量赋值过程是在Activity.attach()方法, 可知其类型为PhoneWindow.

2.2.1 Activity.onTouchEvent

[-> Activity.java]

e5167c9027782d22a21b6353a984b242.png

2.3 superDispatchTouchEvent

[-> PhoneWindow.java]

64268352610833857770d24973d4bf02.png

PhoneWindow的最顶View是DecorView,再交由DecorView处理。而DecorView的父类的父类是ViewGroup,接着调用 ViewGroup.dispatchTouchEvent()方法。为了精简篇幅,有些中间函数调用不涉及关键逻辑,可能会直接跳过。

2.4 ViewGroup.dispatchTouchEvent

40ac51e7f00ac8d55d933ad3d8b04b51.png

4f48b50fd99fe67fc8b0710f84c25daf.png

92a0f9be5e81cb3cf887137679867a2a.png

c78b539b95ad7f3e6837efe1c1f4947b.png

8e4c2d2856e306af918facb14a6a35af.png

dd3b6de97c846861fa29bd71a7bda804.png

2.4.1 onFilterTouchEventForSecurity

8b0f94624abad74e027e0c89e579c4fa.png

根据隐私策略来过滤触摸事件。当返回true,表示继续分发事件;当返回flase,表示该事件应该被过滤掉,不再进行任何分发。

2.4.2 onInterceptTouchEvent

cd9b39ee07178a8b5aafea46e54986e1.png

当返回true,表示该事件被当前视图拦截;

当返回false,继续执行事件分发。

2.4.3 buildOrderedChildList

66141192ee61ae7f98056fae12067a3b.png

获取一个视图组的先序列表,通过虚拟的Z轴来排序。

publicfloatgetZ(){

returngetElevation() + getTranslationZ();}

getZ()用于获取Z轴坐标。屏幕只有x,y坐标,而Z是虚拟的,可通过setElevation(),setTranslationZ()或者setZ()方法来修改Z轴的坐标值。

2.4.4 dispatchTransformedTouchEvent

e0d61ec97f094183738acbfa15a5e097.png

ead851fcb3b08ae5846d28b9da2645eb.png

fb479c59737cd59e0509909e7921778a.png

该方法是ViewGroup真正处理事件的地方,分发子View来处理事件,过滤掉不相干的pointer ids。当子视图为null时,MotionEvent将会发送给该ViewGroup。最终调用View.dispatchTouchEvent方法来分发事件。

2.4.5 addTouchTargetprivate TouchTarget addTouchTarget(View child, int pointerIdBits){ TouchTarget target = TouchTarget.obtain(child, pointerIdBits); target.next = mFirstTouchTarget; mFirstTouchTarget = target;

returntarget;}

调用该方法,获取了TouchTarget,同时mFirstTouchTarget不再为null。

2.5 View.dispatchTouchEvent

[-> View.java]

8af16f6e606536098912fb1c5d9bfde3.png

fd649a35f9c9a2a6784756e671c040cf.png

先由OnTouchListener的OnTouch()来处理事件,当返回True,则消费该事件,否则进入2。

onTouchEvent处理事件,的那个返回True时,消费该事件。否则不会处理

2.5.1 View.onTouchEvent

33ae7d167978a7b7585db615b79a483f.png

cf362ac71d1a938b0de4ebb90d786a84.png

59c87e76e58a40f614620fde282e2e3b.png

3d7fd58ab30e413bd84d5e79f1c6c775.png

三. 总结事件分发流程图:

69ba62b7463795623e6606badef44b90.png

onInterceptTouchEvent返回值true表示事件拦截,onTouch/onTouchEvent返回值true表示事件消费。

触摸事件先交由Activity.dispatchTouchEvent。再一层层往下分发,当中间的ViewGroup都不拦截时,进入最底层的View后,开始由最底层的OnTouchEvent来处理,如果一直不消费,则最后返回到Activity.OnTouchEvent。

ViewGroup才有onInterceptTouchEvent拦截方法。在分发过程中,中间任何一层ViewGroup都可以直接拦截,则不再往下分发,而是交由发生拦截操作的ViewGroup的OnTouchEvent来处理。

子View可调用requestDisallowInterceptTouchEvent方法,来设置disallowIntercept=true,从而阻止父ViewGroup的onInterceptTouchEvent拦截操作。

OnTouchEvent由下往上冒泡时,当中间任何一层的OnTouchEvent消费该事件,则不再往上传递,表示事件已处理。

如果View没有消费ACTION_DOWN事件,则之后的ACTION_MOVE等事件都不会再接收。

只要View.onTouchEvent是可点击或可长按,则消费该事件.

onTouch优先于onTouchEvent执行,上面流程图中省略,onTouch的位置在onTouchEvent前面。当onTouch返回true,则不执行onTouchEvent,否则会执行onTouchEvent。onTouch只有View设置了OnTouchListener,且是enable的才执行该方法。

责任编辑:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值