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

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

文章目录

Android 事件分发 系列文章目录

一、按下触摸事件记录

二、完整的触摸事件处理机制

三、ViewGroup | dispatchTouchEvent 方法返回

四、ViewGroup 事件分发相关源码

五、View 事件分发相关源码





一、按下触摸事件记录


在上一篇博客 【Android 事件分发】事件分发源码分析 ( ViewGroup 事件传递机制 五 ) 中 , 着重分析了 ViewGroup 事件分发中 , 触摸事件没有被消费 , 或被父容器拦截的情况 ;


这里再分析下触摸事件被消费之后的 , 触摸事件记录过程 ;


触摸事件如果成功被消费 , 则 dispatchTransformedTouchEvent 方法返回 true ;


对应的会调用 addTouchTarget 方法 , 创建 TouchTarget 对象 , 赋值给 newTouchTarget 成员变量 ;


newTouchTarget = addTouchTarget(child, idBitsToAssign)



TouchTarget 代表了个触摸事件 , 每个 TouchTarget 中都封装了消费该触摸事件的 View 组件 ;


   

// The touched child view.
        // 当前 View 对象 
        public View child;


TouchTarget 经过优化后 , 以链表形式存储 , 每个 TouchTarget 都定义了一个 next 成员变量 , 指向下一个 TouchTarget 被消费的触摸事件 ;


     

// The next target in the target list.
        // 链表操作 , 该引用指向下一个触摸事件 
        public TouchTarget next;


相关源码 :


@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();
                    }
      // 如果上述事件分发方法 dispatchTransformedTouchEvent 返回 true 
      // 就会创建 newTouchTarget 值 , 该值不会为空 , 同时 mFirstTouchTarget 不为空
      // 如果上述事件分发方法 dispatchTransformedTouchEvent 返回 false 
      // 此时 newTouchTarget 值 , 就会为空 , 同时 mFirstTouchTarget 为空 
                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        // Did not find a child to receive the event.
                        // Assign the pointer to the least recently added target.
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }
    // 如果事件被消费 , 事件分发方法 dispatchTransformedTouchEvent 返回 true 
    // 就会创建 newTouchTarget 值 , 该值不会为空 , 同时 mFirstTouchTarget 不为空
    // 反之
    // 如果上述事件分发方法 dispatchTransformedTouchEvent 返回 false 
    // 此时 newTouchTarget 值 , 就会为空 , 同时 mFirstTouchTarget 为空 
    // 
    // 还有一个逻辑就是 , 如果该事件被父容器拦截 , mFirstTouchTarget 也是 null 值
    // 调用 dispatchTransformedTouchEvent , 但是传入的子组件时 null 
    // 在 dispatchTransformedTouchEvent 方法中触发调用 if (child == null) 分支的 
    // handled = super.dispatchTouchEvent(event) 方法 , 调用父类的事件分发方法 
            // 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);
            } else {
              // 事件被消费的分支 , 事件消费成功 , 会走这个分支 
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
    // 
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                  // 链表式操作 
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
      // 转发下一个触摸事件 
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        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);
            }
        }
        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }
    /**
     * 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;
    }
}




目录
相关文章
|
2月前
|
Ubuntu 开发工具 Android开发
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
本文介绍了在基于Ubuntu 22.04的环境下配置Python 3.9、安装repo工具、下载和同步AOSP源码包以及处理repo同步错误的详细步骤。
118 0
Repo下载AOSP源码:基于ubuntu22.04 环境配置,android-12.0.0_r32
|
16天前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
39 8
|
1月前
|
Android开发 容器
Android经典实战之如何获取View和ViewGroup的中心点
本文介绍了在Android中如何获取`View`和`ViewGroup`的中心点坐标,包括计算相对坐标和屏幕上的绝对坐标,并提供了示例代码。特别注意在视图未完成测量时可能出现的宽高为0的问题及解决方案。
27 7
|
2月前
|
开发工具 git 索引
repo sync 更新源码 android-12.0.0_r34, fatal: 不能重置索引文件至版本 ‘v2.27^0‘。
本文描述了在更新AOSP 12源码时遇到的repo同步错误,并提供了通过手动git pull更新repo工具来解决这一问题的方法。
57 1
|
2月前
|
Android开发 Docker 容器
docker中编译android aosp源码,出现Build sandboxing disabled due to nsjail error
在使用Docker编译Android AOSP源码时,如果遇到"Build sandboxing disabled due to nsjail error"的错误,可以通过在docker run命令中添加`--privileged`参数来解决权限不足的问题。
183 1
|
2月前
|
开发工具 uml git
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
本文分享了下载AOSP源码的方法,包括如何使用repo工具和处理常见的repo sync错误,以及配置Python环境以确保顺利同步特定版本的AOSP代码。
169 0
AOSP源码下载方法,解决repo sync错误:android-13.0.0_r82
|
2月前
|
Java Android开发 芯片
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
本文介绍了如何将基于全志H713芯片的AOSP Android源码导入Android Studio以解决编译和编码问题,通过操作步骤的详细说明,展示了在Android Studio中利用代码提示和补全功能快速定位并修复编译错误的方法。
49 0
使用Android Studio导入Android源码:基于全志H713 AOSP,方便解决编译、编码问题
|
2月前
|
Android开发
我的Android 进阶修炼(1): AOSP源码根目录结构
本文介绍了AOSP源码的根目录结构,提供了基于MTK9269 Android 9.0源码的目录说明,帮助读者了解AOSP源码的组织方式和各目录的功能。
64 0
我的Android 进阶修炼(1): AOSP源码根目录结构
|
2月前
|
API 开发工具 Android开发
Android源码下载
Android源码下载
157 0
|
2月前
|
消息中间件 存储 Java
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
Android 消息处理机制估计都被写烂了,但是依然还是要写一下,因为Android应用程序是通过消息来驱动的,Android某种意义上也可以说成是一个以消息驱动的系统,UI、事件、生命周期都和消息处理机制息息相关,并且消息处理机制在整个Android知识体系中也是尤其重要,在太多的源码分析的文章讲得比较繁琐,很多人对整个消息处理机制依然是懵懵懂懂,这篇文章通过一些问答的模式结合Android主线程(UI线程)的工作原理来讲解,源码注释很全,还有结合流程图,如果你对Android 消息处理机制还不是很理解,我相信只要你静下心来耐心的看,肯定会有不少的收获的。
118 3
Android面试高频知识点(2) 详解Android消息处理机制(Handler)
下一篇
无影云桌面