当前位置: 首页 > news >正文

android 事件分发源码分析

1)DOWN事件触发时重置标志位;

2)判断是否拦截事件(根据FLAG_DISALLOW_INTERCEPT标志和onInterceptTouchEvent返回值);

3)遍历子View寻找能处理事件的View,通过dispatchTransformedTouchEvent询问子View;

4)将处理事件的子View保存到mFirstTouchTarget链表;

5)后续事件根据mFirstTouchTarget和拦截状态决定分发路径。当父容器拦截时,会向子View发送CANCEL事件并清空mFirstTouchTarget。整个过程实现了从父容器到子View的事件分发与拦截机制

  • 置空标志位置
   // 在DOWN事件的时候会置空标志位置if (actionMasked == MotionEvent.ACTION_DOWN) {// 清掉旧 TouchTarget,清空mFirstTouchTargetcancelAndClearTouchTargets(ev);// 重置 FLAG_DISALLOW_INTERCEPT 等resetTouchState();}private void cancelAndClearTouchTargets(MotionEvent event) {//清空mFirstTouchTargetclearTouchTargets();}//清空mFirstTouchTargetprivate void clearTouchTargets() {ViewGroup.TouchTarget target = mFirstTouchTarget;if (target != null) {do {ViewGroup.TouchTarget next = target.next;target.recycle();target = next;} while (target != null);mFirstTouchTarget = null;}}
  • 第一块代码,判断事件是否由父容器拦截
  //是否拦截标志final boolean intercepted;//判断是否是DOWN事件,或者是mFirstTouchTarget第一个处理事件的Viewif (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {//requestDisallowInterceptTouchEvent决定这里的属性,true这里为true,false这里为falsefinal boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;if (!disallowIntercept) {//父容器是否拦截事件intercepted = onInterceptTouchEvent(ev);ev.setAction(action); // restore action in case it was changed} else {intercepted = false;}} else {// There are no touch targets and this action is not an initial down// so this view group continues to intercept touches.intercepted = true;}
  • 第二块代码,决定是由父容器还是子View处理事件
     ViewGroup.TouchTarget newTouchTarget = null;boolean alreadyDispatchedToNewTouchTarget = false;//判断是否是拦截事件if (!canceled && !intercepted) {//单手指DOWN,多手指ACTION_POINTER_DOWN,鼠标DOWN ACTION_HOVER_MOVEif (actionMasked == MotionEvent.ACTION_DOWN|| (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {//这里newTouchTarget=null,判断是否存在子Viewif (newTouchTarget == null && childrenCount != 0) {final float x =isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);final float y =isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);// Find a child that can receive the event.// Scan children from front to back.final ArrayList<View> preorderedList = buildTouchDispatchChildList();final boolean customOrder = preorderedList == null&& isChildrenDrawingOrderEnabled();final View[] children = mChildren;//开始遍历子Viewfor (int i = childrenCount - 1; i >= 0; i--) {//确定该点位落在那个Viewif (!child.canReceivePointerEvents()|| !isTransformedTouchPointInView(x, y, child, null)) {ev.setTargetAccessibilityFocus(false);continue;}newTouchTarget = getTouchTarget(child);if (newTouchTarget != null) {// Child is already receiving touch within its bounds.// Give it the new pointer in addition to the ones it is handling.newTouchTarget.pointerIdBits |= idBitsToAssign;break;}resetCancelNextUpFlag(child);//找到落点位置的View,开始询问View是否处理事件if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {// Child wants to receive touch within its bounds.mLastTouchDownTime = ev.getDownTime();if (preorderedList != null) {// childIndex points into presorted list, find original indexfor (int j = 0; j < childrenCount; j++) {if (children[childIndex] == mChildren[j]) {mLastTouchDownIndex = j;break;}}} else {mLastTouchDownIndex = childIndex;}mLastTouchDownX = ev.getX();mLastTouchDownY = ev.getY();//有View处理事件,开始修改标志位置,将mFirstTouchTarget设置为当前处理的View,newTouchTarget = mFirstTouchTargetnewTouchTarget = addTouchTarget(child, idBitsToAssign);//表示down事件已经有View处理alreadyDispatchedToNewTouchTarget = true;break;}// The accessibility focus didn't handle the event, so clear// the flag and do a normal dispatch to all children.ev.setTargetAccessibilityFocus(false);}if (preorderedList != null) preorderedList.clear();}}//标准位进行赋值,mFirstTouchTarget = target, target.next = null,mFirstTouchTarget.next = nullprivate ViewGroup.TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {final ViewGroup.TouchTarget target = ViewGroup.TouchTarget.obtain(child, pointerIdBits);//这里mFirstTouchTarget在DOWN的时候置空了,这里为空target.next = mFirstTouchTarget;//mFirstTouchTarget赋值为当前处理事件的ViewmFirstTouchTarget = target;return target;}}dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {// Perform any necessary transformations and dispatch.if (child == null) {handled = super.dispatchTouchEvent(transformedEvent);} else {//询问子View是否处理事件final float offsetX = mScrollX - child.mLeft;final float offsetY = mScrollY - child.mTop;transformedEvent.offsetLocation(offsetX, offsetY);if (! child.hasIdentityMatrix()) {transformedEvent.transform(child.getInverseMatrix());}handled = child.dispatchTouchEvent(transformedEvent);}}
  • 第三块代码,对其他事件继续处理
 // Check for cancelation.final boolean canceled = resetCancelNextUpFlag(this)|| actionMasked == MotionEvent.ACTION_CANCEL;//没有子View处理事件,将事件交个父容器,询问父容器是否需要处理事件if (mFirstTouchTarget == null) {//注释1:canceled = falsehandled = dispatchTransformedTouchEvent(ev, canceled, null,ViewGroup.TouchTarget.ALL_POINTER_IDS);} else {ViewGroup.TouchTarget predecessor = null;ViewGroup.TouchTarget target = mFirstTouchTarget;//mFirstTouchTarget = target, target.next = null,mFirstTouchTarget.next = null//这里不为空,执行该方法while (target != null) {//next 未空,下一次将不执行final ViewGroup.TouchTarget next = target.next;//DOWN alreadyDispatchedToNewTouchTarget = true 有View处理事件 alreadyDispatchedToNewTouchTarget = true,newTouchTarget = target = mFirstTouchTargetif (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {handled = true;} else {//DOWN事件没有处理获取其他事件 alreadyDispatchedToNewTouchTarget = falsefinal boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;//注释2:对已经处理的事件进行分发if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {handled = true;}//当MOVE的时候,父容器拦截事件,将事件置为CANCEL事件后,将mFirstTouchTarget置空if (cancelChild) {if (predecessor == null) {mFirstTouchTarget = next;} else {predecessor.next = next;}target.recycle();target = next;continue;}}predecessor = target;target = next;}}// Update list of touch targets for pointer up or cancel, if needed.if (canceled|| actionMasked == MotionEvent.ACTION_UP|| actionMasked == MotionEvent.ACTION_HOVER_MOVE) {resetTouchState();} else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {final int actionIndex = ev.getActionIndex();final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);removePointersFromTouchTargets(idBitsToRemove);}//注释1://canceled = falsehandled = dispatchTransformedTouchEvent(ev, canceled, null,ViewGroup.TouchTarget.ALL_POINTER_IDS);private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {//child = null,询问父容器是否处理事件if (child == null) {handled = super.dispatchTouchEvent(transformedEvent);} else {final float offsetX = mScrollX - child.mLeft;final float offsetY = mScrollY - child.mTop;transformedEvent.offsetLocation(offsetX, offsetY);if (! child.hasIdentityMatrix()) {transformedEvent.transform(child.getInverseMatrix());}handled = child.dispatchTouchEvent(transformedEvent);}}//注释2:final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)cancelChild =false;private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {if (child == null) {handled = super.dispatchTouchEvent(transformedEvent);} else {//询问子View 是否处理事件final float offsetX = mScrollX - child.mLeft;final float offsetY = mScrollY - child.mTop;transformedEvent.offsetLocation(offsetX, offsetY);if (! child.hasIdentityMatrix()) {transformedEvent.transform(child.getInverseMatrix());}handled = child.dispatchTouchEvent(transformedEvent);}}cancelChild = true;private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,View child, int desiredPointerIdBits) {//父类View拦截了事件,将事件改成cancel下发给Viewif (cancel || oldAction == MotionEvent.ACTION_CANCEL) {event.setAction(MotionEvent.ACTION_CANCEL);if (child == null) {handled = super.dispatchTouchEvent(event);} else {handled = child.dispatchTouchEvent(event);}event.setAction(oldAction);return handled;}}
  • 事件分发场景1

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回false,

条件3:父容器onTouch返回false,

  • 事件分发2

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回true,

条件3:父容器onTouch返回false,

  • 事件分发3

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回false,

条件3:父容器onTouch返回true,

  • 事件分发4

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回true,

条件3:父容器onTouch返回true,

同事件分发2,会首先响应子View的事件处理,响应后不再响应父容器的事件响应

  • 事件分发5

如果DOWN事件是父容器拦截了,那么子View将不会再收到事件

DOWN事件,

条件1:父容器onInterceptTouchEvent返回true,

条件2:子View dispatchTouchEvent返回true或者false,

条件3:父容器onTouch返回true或者false,

  • 事件分发6

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View requestDisallowInterceptTouchEvent设置成true

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true或者false,

MOVE事件1,

条件1:父容器onInterceptTouchEvent返回true,

条件2:子View requestDisallowInterceptTouchEvent设置成false

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true,

MOVE事件2,

条件1:父容器onInterceptTouchEvent返回true,

条件2:requestDisallowInterceptTouchEvent设置成false

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true,

MOVE事件3,

条件1:父容器onInterceptTouchEvent返回true,

条件2:requestDisallowInterceptTouchEvent设置成false

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true,

子View只要在DOWN事件返回true就可以接收其他所有事件

在DOWN事件返回true的时候,会把响应的View保存到派发链表mFirstTouchTarget中,接下来的事件,只要父类不拦截,都会派发事件给当前mFirstTouchTarget保存的View,如果父容器有拦截,就会传递CANCEL事件给mFirstTouchTarget保存的View,并且把mFirstTouchTarget置空,以后的所有事件都会传给父容器

http://www.dtcms.com/a/351289.html

相关文章:

  • STL库——vector(类函数学习)
  • 【51单片机】萌新持续学习中《矩阵 密码锁 点阵屏》
  • 矩阵初等变换的几何含义
  • 血缘元数据采集开放标准:OpenLineage Integrations Apache Spark Configuration Usage
  • 重写BeanFactory初始化方法并行加载Bean
  • 信息网络安全视角下的在线问卷调查系统设计与实践(国内问卷调查)
  • 记一个Mudbus TCP 帮助类
  • Linux 内核 Workqueue 原理与实现及其在 KFD SVM功能的应用
  • LeetCode - 844. 比较含退格的字符串
  • LeetCode 438. 找到字符串中所有的字母异位词
  • 微算法科技(NASDAQ:MLGO)通过修改 Grover 算法在可重构硬件上实现动态多模式搜索
  • LeetCode - 946. 验证栈序列
  • 智慧园区:从技术赋能到价值重构,解锁园区运营新范式
  • 透视光合组织大会:算力生态重构金融AI落地新实践
  • 亚马逊类目合规风暴:高压清洗机品类整顿背后的运营重构与风险防御
  • 便携屏选购指南:常见作用、移动性优势及多场景应用详解
  • 前端性能优化新维度:渲染流水线深度解析
  • 【前端开发实战】从零开始开发Chrome浏览器扩展 - 快乐传播者项目完整教程
  • DeepSeek分析
  • spring如何通过实现BeanPostProcessor接口计算并打印每一个bean的加载耗时
  • 【数据结构】树和二叉树——二叉树
  • pytorch_grad_cam 库学习笔记—— Ablation-CAM 算法的基类 AblationCAM 和 AblationLayer
  • OneCode RAD:揭秘前端开发的配置化魔法
  • 【RAGFlow代码详解-14】知识图谱处理
  • Linux之SELinux 概述、SSH 密钥登录、服务器初始化
  • IUV5G专网排障(下)
  • 开源大模型本地部署
  • [Mysql数据库] 知识点总结3
  • 基于Android的电影院订票选座系统、基于Android的电影院管理系统app#基于Android的电影在线订票系统
  • 玩转QEMU硬件模拟器 - vexpress-a9开发板模拟开发