【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )(一)

简介: 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 )(一)

文章目录

Android 事件分发 系列文章目录

前言

一、ViewGroup | dispatchTransformedTouchEvent 方法返回值含义

二、记录被消费的触摸事件

三、触摸事件没有被消费的调用链分析

四、触摸事件被拦截的调用链分析

五、ViewGroup 事件分发相关源码

六、View 事件分发相关源码

总结

前言

接上一篇博客 【Android 事件分发】事件分发源码分析 ( View 事件传递机制 ) , 继续分析 ViewGroup 的事件分发机制后续代码 ;


Activity 事件传递 , 最终调用到了 ViewGroup 的 dispatchTouchEvent 方法 , 在该方法中有调用了 View 的 dispatchTouchEvent 方法 ;


View 的 dispatchTouchEvent 调用完毕后 , 会返回 true 或 false 结果 ;


那么继续回到 ViewGroup 的 dispatchTransformedTouchEvent 方法 , 在该方法中 , 也就是遍历 ViewGroup 子组件时 , 调用遍历的子组件 View 的 dispatchTouchEvent 方法 , 处理相关触摸和点击相关的操作 ;


本博客继续分析 ViewGroup 的后续调用链 ;






一、ViewGroup | dispatchTransformedTouchEvent 方法返回值含义


在 ViewGroup | dispatchTouchEvent 方法中 , 调用 ViewGroup | dispatchTransformedTouchEvent 方法进行事件的转发 , 主要是遍历子组件调用子组件的触摸方法 ;


在 ViewGroup | dispatchTransformedTouchEvent 方法中 , 调用了 View 子组件的 View | dispatchTouchEvent 方法 , 该方法返回 true/false , 那么对应的 ViewGroup | dispatchTransformedTouchEvent 方法也会返回相同的值 ;


由下面的代码可知 , handled 是子组件 child.dispatchTouchEvent 调用返回值 , 返回结果就是 handled , 然后有将该值直接返回 , 说明 View 子组件的 dispatchTouchEvent 调用结果 , 就是 ViewGroup 的 dispatchTransformedTouchEvent 方法的返回结果 ;


ViewGroup | dispatchTransformedTouchEvent 方法的返回值含义 :


① 如果返回 true , 证明 子组件 View 已经将事件消费完毕 , 后续不需要再继续执行遍历了 ;

② 如果返回 false , 证明 子组件并没有将事件消费 , 后续还可以进行触发 ;



public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
  ...
                if (child == null) {
                  // 被遍历的 child 子组件为空 
                  // 调用父类的分发方法 
                    handled = super.dispatchTouchEvent(event);
                } else {
                  // 被遍历的 child 子组件不为空 
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);
      // 子组件分发触摸事件 
      // 此处调用的是 View 组件的 dispatchTouchEvent 方法 ; 
                    handled = child.dispatchTouchEvent(event);
                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
  ...
  }
}






二、记录被消费的触摸事件


如果 ViewGroup | dispatchTransformedTouchEvent 方法的返回值为 true , 则会触发记录已经被消费的事件的分支 ; 如果返回 false , 则不会触发 ;


// 正式开始分发触摸事件
// 处理以下两种情况 : 
// ① 情况一 : 子控件触摸事件返回 true 
// ② 情况二 : 子控件触摸事件返回 false 
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {


记录时 , 同时会创建 TouchTarget , 并赋值给 ViewGroup | dispatchTouchEvent | newTouchTarget 局部变量 ;


// 记录消费事件 
// 添加触摸目标 
newTouchTarget = addTouchTarget(child, idBitsToAssign);


调用 addTouchTarget 方法 , 创建被消费的事件 , 并将创建的值赋值给了 ViewGroup | mFirstTouchTarget 成员 ; 第一次消费事件被赋值 ;


 

private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }


完整代码如下 :


@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
  ...
                            // 正式开始分发触摸事件
                            // 处理以下两种情况 : 
                            // ① 情况一 : 子控件触摸事件返回 true 
                            // ② 情况二 : 子控件触摸事件返回 false 
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                // 如果返回值为 true , 说明该事件已经被消费了 
                                // 此时记录这个已经被消费的事件 
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                // 记录消费事件 
                                // 添加触摸目标 
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                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();
  ...
  }
    /**
     * Adds a touch target for specified child to the beginning of the list.
     * Assumes the target child is not already present.
     */
    private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }
}





三、触摸事件没有被消费的调用链分析


如果上述事件分发方法 dispatchTransformedTouchEvent 返回 true , 就会创建 newTouchTarget 值 , 该值不会为空 , 同时 mFirstTouchTarget 不为空 ;


如果上述事件分发方法 dispatchTransformedTouchEvent 返回 false , 此时 newTouchTarget 值 , 就会为空 , 同时 mFirstTouchTarget 为空 ;


如果 ViewGroup | dispatchTransformedTouchEvent 方法的返回值为 false , 则不会记录事件消费 ; 也就不会调用 ViewGroup | addTouchTarget 方法 , 此时


ViewGroup | dispatchTouchEvent | newTouchTarget 局部变量

ViewGroup | mFirstTouchTarget 成员变量


为空 ;


 

/**
     * Adds a touch target for specified child to the beginning of the list.
     * Assumes the target child is not already present.
     */
    private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }


当执行到 if (mFirstTouchTarget == null) 分支时 , 会命中该分支 , 进而调用 dispatchTransformedTouchEvent(ev, canceled, null, TouchTarget.ALL_POINTER_IDS) 方法 , 在 dispatchTransformedTouchEvent 方法中 , 如果 child 为空 , 则会调用父类的 super.dispatchTouchEvent 方法 ;


@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
  ...
                            // 正式开始分发触摸事件
                            // 处理以下两种情况 : 
                            // ① 情况一 : 子控件触摸事件返回 true 
                            // ② 情况二 : 子控件触摸事件返回 false 
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                // 如果返回值为 true , 说明该事件已经被消费了 
                                // 此时记录这个已经被消费的事件 
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                // 记录消费事件 
                                // 添加触摸目标 
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                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();
            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            }
  ...
  }
    /**
     * Adds a touch target for specified child to the beginning of the list.
     * Assumes the target child is not already present.
     */
    private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
        final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
        target.next = mFirstTouchTarget;
        mFirstTouchTarget = target;
        return target;
    }
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        final int oldAction = event.getAction();
        if (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;
        }
        ...
  }
}


ViewGroup 继承自 View , 在 ViewGroup 中调用 super.dispatchTouchEvent 方法 , 就是调用 View 的 dispatchTouchEvent 方法 ;


super.dispatchTouchEvent(event) 方法消费的是父容器自己的事件 ;


child.dispatchTouchEvent(event) 方法消费的是子组件的事件 ;



目录
相关文章
|
存储 安全 Android开发
探索Android与iOS的隐私保护机制
在数字化时代,移动设备已成为我们生活的一部分,而隐私安全是用户最为关注的问题之一。本文将深入探讨Android和iOS两大主流操作系统在隐私保护方面的策略和实现方式,分析它们各自的优势和不足,以及如何更好地保护用户的隐私。
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
203 2
|
7月前
|
消息中间件 Android开发
Android Handler的使用方式以及其机制的简单介绍
Handler 是 Android 中实现线程间通信的重要机制,可传递任意两线程数据。常用场景包括子线程向主线程(UI 线程)传递结果,以及主线程向子线程发送消息。其核心涉及四个类:Handler(发送/接收消息)、Message(消息载体)、MessageQueue(消息队列)和 Looper(消息循环泵)。基本流程为:Handler 发送 Message 至 MessageQueue,Looper 从队列中按 FIFO 取出并处理。
234 0
|
Linux Android开发 iOS开发
深入探索Android与iOS的多任务处理机制
在移动操作系统领域,Android和iOS各有千秋,尤其在多任务处理上展现出不同的设计理念和技术实现。本文将深入剖析两大平台在后台管理、资源分配及用户体验方面的策略差异,揭示它们如何平衡性能与电池寿命,为用户带来流畅而高效的操作体验。通过对比分析,我们不仅能够更好地理解各自系统的工作机制,还能为开发者优化应用提供参考。
|
算法 Linux 调度
深入探索安卓系统的多任务处理机制
【10月更文挑战第21天】 本文旨在为读者提供一个关于Android系统多任务处理机制的全面解析。我们将从Android操作系统的核心架构出发,探讨其如何管理多个应用程序的同时运行,包括进程调度、内存管理和电量优化等方面。通过深入分析,本文揭示了Android在处理多任务时所面临的挑战以及它如何通过创新的解决方案来提高用户体验和设备性能。
754 1
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
232 1
|
存储 安全 数据安全/隐私保护
探索安卓与iOS的隐私保护机制####
【10月更文挑战第15天】 本文深入剖析了安卓和iOS两大操作系统在隐私保护方面的策略与技术实现,旨在揭示两者如何通过不同的技术手段来保障用户数据的安全与隐私。文章将逐一探讨各自的隐私控制功能、加密措施以及用户权限管理,为读者提供一个全面而深入的理解。 ####
835 1
|
消息中间件 存储 Java
Android消息处理机制(Handler+Looper+Message+MessageQueue)
Android消息处理机制(Handler+Looper+Message+MessageQueue)
452 2
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
314 8

热门文章

最新文章