Android事件的分发机制,Android事件分发机制全面解析

事件分发机制

事件分发机制的两个阶段:

分发:事件从父视图往子视图分发,被拦截后不再传递,进入回溯阶段

回溯:事件从子视图往父视图回溯,被消费后不再回溯

关键方法:

ViewGroup.dispatchTouchEvent 往子视图分发事件

ViewGroup.onInterceptTouchEvent 返回 true 表示拦截分发事件,不再传递,进入当前视图 onTouchEvent

View.dispatchTouchEvent 默认事件分发,调用 onTouchEvent

View.onTouchEvent 通常重载此方法处理事件,返回 true 表示消费事件,不再传递,返回 false 往上回溯

ViewParent.requestDisallowInterceptTouchEvent(true) 可以确保事件分发到子视图前不被拦截

假设视图层次为 A.B.C.D,事件分发回溯默认过程为:

A.dispatchTouchEvent

B.dispatchTouchEvent

C.dispatchTouchEvent

D.dispatchTouchEvent

D.onTouchEvent

C.onTouchEvent

B.onTouchEvent

A.onTouchEvent

假设 B 拦截了事件:

A.dispatchTouchEvent

B.dispatchTouchEvent -> B.onInterceptTouchEvent

B.onTouchEvent

A.onTouchEvent

假设 C.onTouchEvent 消费了事件:

A.dispatchTouchEvent

B.dispatchTouchEvent

C.dispatchTouchEvent

D.dispatchTouchEvent

D.onTouchEvent

C.onTouchEvent

事件分发机制伪代码:

class Activity {

fun dispatchTouchEvent(ev) {

if (parent.dispatchTouchEvent(ev)) {

return true

}

return onTouchEvent(ev)

}

fun onTouchEvent(ev):Boolean {...}

}

class ViewGroup : View {

fun dispatchTouchEvent(ev) {

var handled = false

if (!onInterceptTouchEvent(ev)) {

handled = child.dispatchTouchEvent(ev)

}

return handled || super.dispatchTouchEvent(ev)

}

fun onInterceptTouchEvent(ev):Boolean {...}

fun onTouchEvent(ev):Boolean {...}

}

class View {

fun dispatchTouchEvent(ev) {

var result = false

if (handleScrollBarDragging(ev)) {

result = true

}

if (!result && mOnTouchListener.onTouch(ev)) {

result = true

}

if (!result && onTouchEvent(ev)) {

result = true

}

return result

}

fun onTouchEvent(ev):Boolean {...}

}

ViewGroup.dispatchTouchEvent 源码分析

1.开始:ACTION_DOWN 事件开始一个新的事件序列,清除之前触摸状态

2.拦截:

2.1. 非 ACTION_DOWN 事件如果当前没有子视图消费事件,表示事件序列已被拦截

2.2. 事件未被拦截且子视图未申请禁止拦截时,再通过 onInterceptTouchEvent 尝试拦截事件

3.分发:如果事件未被拦截也未被取消,就遍历子视图分发事件,并寻找当前事件的触摸目标

3.1. 在触摸目标链表中找到了可以消费当前事件的视图触摸目标 -> 将其标记为当前触摸目标,延迟到步骤4分发事件给它

3.2. 一个不在触摸目标链表中的视图消费了事件 -> 将其标记为当前触摸目标,并设置为触摸目标链表表头

3.3. 未找到消费当前事件的视图,但触摸目标链表不为空 -> 将触摸目标链表末端标记为当前触摸目标

4.分发:触摸目标链表不为空,则遍历触摸目标链尝试传递事件或取消触摸目标(事件被拦截)

5.回溯:触摸目标链表为空(当前没有子视图消耗事件序列),则将事件转发给基类 dispatchTouchEvent 处理

注:触摸目标(ViewGourp.TouchTarget) 描述一个被触摸的子视图和它捕获的指针ids

public boolean dispatchTouchEvent(MotionEvent ev) {

// 省略代码 ...

boolean handled = false;

if (onFilterTouchEventForSecurity(ev)) {

if (actionMasked == MotionEvent.ACTION_DOWN) {

// 1. `ACTION_DOWN` 事件开始一个新的事件序列,清除之前触摸状态 ...

}

// 省略代码 ...

final boolean intercepted;

// 2. 拦截

if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) {

final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

if (!disallowIntercept) {

// 2.2. 事件未被拦截且子视图未申请禁止拦截时,再通过 onInterceptTouchEvent 尝试拦截事件

intercepted = onInterceptTouchEvent(ev);

// 省略代码 ...

} else {

intercepted = false;

}

} else {

// 2.1. 非 `ACTION_DOWN` 事件如果当前没有子视图消费事件,表示事件序列已被拦截

intercepted = true;

}

// 省略代码 ...

if (!canceled && !intercepted) {

// 省略代码 ...

// 3. 分发:如果事件未被拦截也未被取消,就遍历子视图分发事件,并寻找当前事件的触摸目标

for (int i = childrenCount - 1; i >= 0; i--) {

// 省略代码 ...

newTouchTarget = getTouchTarget(child);

if (newTouchTarget != null) {

// 3.1. 在触摸目标链表中找到了可以消费当前事件的视图触摸目标 -> 将其标记为当前触摸目标,延迟到步骤4分发事件给它

// 省略代码 ...

break;

}

if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {

// 省略代码 ...

// 3.2. 一个不在触摸目标链表中的视图消费了事件 -> 将其标记为当前触摸目标,并设置为触摸目标链表表头

newTouchTarget = addTouchTarget(child, idBitsToAssign);

alreadyDispatchedToNewTouchTarget = true;

break;

}

// 省略代码 ...

}

if (newTouchTarget == null && mFirstTouchTarget != null) {

// 3.3. 未找到消费当前事件的视图,但触摸目标链表不为空 -> 将触摸目标链表末端标记为当前触摸目标

newTouchTarget = mFirstTouchTarget;

while (newTouchTarget.next != null) {

newTouchTarget = newTouchTarget.next;

}

newTouchTarget.pointerIdBits |= idBitsToAssign;

}

// 省略代码 ...

}

// Dispatch to touch targets.

if (mFirstTouchTarget == null) {

// 5. 回溯:触摸目标链表为空(当前没有子视图消耗事件序列),则将事件转发给基类 dispatchTouchEvent 处理

handled = dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS);

} else {

// 省略代码 ...

// 4. 分发:触摸目标链表不为空,则遍历触摸目标链尝试传递事件或取消触摸目标(事件被拦截)

TouchTarget target = mFirstTouchTarget;

while (target != null) {

final TouchTarget next = target.next;

// 省略代码 ...

if (dispatchTransformedTouchEvent(ev, cancelChild, target.child, target.pointerIdBits)) {

handled = true;

}

// 省略代码 ...

target = next;

}

}

// 省略代码 ...

}

// 省略代码 ...

return handled;

}

View.dispatchTouchEvent 和 View.onTouchEvent 源码分析

滚动条消费鼠标事件

OnTouchListener 消费触摸事件

onTouchEvent 消费触摸事件

TouchDelegate 消费触摸事件

public boolean dispatchTouchEvent(MotionEvent event) {

// 省略代码 ...

boolean result = false;

// 省略代码 ...

if (onFilterTouchEventForSecurity(event)) {

// 滚动条消费鼠标事件

if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {

result = true;

}

// OnTouchListener 消费触摸事件

ListenerInfo li = mListenerInfo;

if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED && li.mOnTouchListener.onTouch(this, event)) {

result = true;

}

// View默认的事件处理逻辑,事件可能在其中被设置的 TouchDelegate 消费

if (!result && onTouchEvent(event)) {

result = true;

}

}

// 省略代码 ...

return result;

}

public boolean onTouchEvent(MotionEvent event) {

// 省略代码 ...

if (mTouchDelegate != null) {

// TouchDelegate 消费触摸事件

if (mTouchDelegate.onTouchEvent(event)) {

return true;

}

}

// 省略代码 ...

return false;

}

以上就是Android事件分发机制全面解析的详细内容,更多关于Android事件分发机制的资料请关注脚本之家其它相关文章!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值