时间:2021-03-27来源:www.pcxitongcheng.com作者:电脑系统城
事件分发机制的两个阶段:
关键方法:
假设视图层次为 A.B.C.D,事件分发回溯默认过程为:
1 2 3 4 5 6 7 8 |
A.dispatchTouchEvent B.dispatchTouchEvent C.dispatchTouchEvent D.dispatchTouchEvent D.onTouchEvent C.onTouchEvent B.onTouchEvent A.onTouchEvent |
假设 B 拦截了事件:
1 2 3 4 |
A.dispatchTouchEvent B.dispatchTouchEvent -> B.onInterceptTouchEvent B.onTouchEvent A.onTouchEvent |
假设 C.onTouchEvent 消费了事件:
1 2 3 4 5 6 |
A.dispatchTouchEvent B.dispatchTouchEvent C.dispatchTouchEvent D.dispatchTouchEvent D.onTouchEvent C.onTouchEvent |
事件分发机制伪代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
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 {...} } |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
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; } |
TouchDelegate 消费触摸事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
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事件分发机制全面解析的详细内容
2024-04-11
台式机电脑如何连接外接显示器2024-04-11
小新系列打印机手机配置网络的方法教程2024-04-11
Thinkpad 笔记本F1-F12快捷键分别是什么功能ThinkPad蓝牙鼠标如何配对解答步骤41U5008鼠标驱动官网地址: https://support.lenovo.com/en_US/downloads/detail.page?&LegacyDocID=MIGR-67201 第一种方式是比较传统的:使...
2024-04-11
故障现象: USB设备U盘、移动硬盘等插入后提示无法识别的设备,确认设备本身正常,设备可加电,或插入设备后加电但无任何反应,无法使用。新型号机器多表现为黄色USB接口存在此问题,...
2024-04-11