首页 > 编程语言 >事件分发源码分析

事件分发源码分析

时间:2023-03-01 17:35:02浏览次数:38  
标签:分发 null ViewGroup 源码 事件 ev final View

1.Activity对事件的分发过程

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

点击事件用MotionEvent表示,事件先传递给Activity,由它的dispatchTouchEvent进行事件分发,具体由Window来完成,Window会将事件传递给Decor view(当前界面底层容器,即setContextView所设置的View的父容器)。

先分析dispatchTouchEvent:

可以看出,事件首先交给Activity附属的Window进行分发,返回true,整个循环结束,false表示没人处理该事件。所有View的onTouchEvent都返回false,那么Activity的onTouchEvent就会被调用。

2.接下来看事件是怎么传递给Group View的

Window的实现类是PhoneWindow类,所以再看PhoneWindow如何处理事件:

/frameworks/base/core/java/android/view/Window.java

/**
     * Used by custom windows, such as Dialog, to pass the touch screen event
     * further down the view hierarchy. Application developers should
     * not need to implement or call this.
     *
     */
    public abstract boolean superDispatchTouchEvent(MotionEvent event);

/frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {
mDecor = (DecorView) preservedWindow.getDecorView();
@Override public boolean superDispatchTouchEvent(MotionEvent event) { return mDecor.superDispatchTouchEvent(event); }
    @Override
    public final @NonNull View getDecorView() {
        if (mDecor == null || mForceDecorInstall) {
            installDecor();
        }
        return mDecor;
    }

通过(ViewGroup)getWindow().getDecorview.findViewById(android.R.id.context)).getChildAt(0)获取Activity设置的View,mDecor为getWindow().getDecorView()返回的View。通过setContentView设置View是它的一个子View。所以事件首先会传递给DecroView,由于DecorView继承自FrameLayout,且为父View,所以事件最终会传递给View。
\frameworks\base\core\java\com\android\internal\policy\DecorView.java

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
public boolean superDispatchTouchEvent(MotionEvent event) { return super.dispatchTouchEvent(event); }

进入 FrameLayout   /frameworks/base/core/java/android/widget/FrameLayout.java

3.顶级View对点击事件的分发过程

分发过程:首先一个事件到顶级的View(ViewGroup),会调用ViewGroup的dispatchTouchEvent,如果拦截,ViewGroup的onInterceptTouchEvent返回true,则事件由ViewGroup处理,具体会根据事件处理的优先级,消耗事件。返回false,则事件将传递给下一级View,依次类推

3.1.ViewGroup对事件的分发

首先看ViewGroup的dispatchTouchEvent方法


// Handle an initial down.
if (actionMasked == MotionEvent.ACTION_DOWN) {
// Throw away all previous state when starting a new touch gesture.
// The framework may have dropped the up or cancel event for the previous gesture
// due to an app switch, ANR, or some other state change.
cancelAndClearTouchTargets(ev);
resetTouchState();
}


final boolean intercepted; if (actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null) { final 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; }

可见ViewGroup在ACTION_DOWN或者mFirstTouchTarget != null,其中mFirstTouchTarget != null,即,当事件由VIewGroup子控件处理时,mFirstTouchTarget会被指向子元素。
当然还有一种特殊情况:通过requestDisallowInterceptTouchEvent来设置的FLAG_DISALLOW_INTERCEPT标记位,它设置后ViewGroup无法拦截除了ACTIOND_DOWN以外的点击事件。为什么是ACTION_DOWN以外?是因为ViewGroup接收到ACTION_DOWN时,会重置一次标记位,导致子View设置无效。ViewGroup对待ACTION_DOWN事件,会调用自己的onInterceptTouchEvent方法来拦截

上面代码可以看出,当ViewGroup决定拦截事件后,后续的事件都将默认交给它处理,并且不会再调用onInterceptTouchEvent…
参考价值有两点:
第一、onInterceptTouchEvent 不是每次都会调用,如果我们需要在前一节点处理事务,要选择dispatchTouchEvent方法,但必须续保事件可以传到ViewGroup中。
第二、FLAG_DISALLOW_INTERCEPT标记位,可以帮我们解决滑动冲突的问题。

3.2ViewGroup对事件不拦截,事件如何向下分发

                    final int childrenCount = mChildrenCount;
                    if (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;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);
                            if (!child.canReceivePointerEvents()
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                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);
                            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 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();
                    }

上述代码可见,首先遍历ViewGroup的每个子View,判断子view是否得到了点击事件。判断标准:1.点击事件的坐标是否落到了子元素的区域内;2.子元素是否在播动画如果满足这两个条件,则交由子元素处理。而dispatchTransformedTouchEvent实际调用了子元素的dispatchTouchEvent,这样就把事件传递给子View了,也将完成了一次循环。

 

标签:分发,null,ViewGroup,源码,事件,ev,final,View
From: https://www.cnblogs.com/wanglongjiang/p/17169050.html

相关文章

  • java List 源码
    概述底层通过数组实现,所以查询/更新效率很高,删除/增加跟位置相关,除未实现同步外和Vector大致相同扩容publicbooleanadd(Ee){//数组扩容,不是一定要扩容,需要......
  • 内容分发网络 CDN
    介绍CDN内容分发网络(英语:ContentDeliveryNetwork或ContentDistributionNetwork,缩写:CDN)是建立并覆盖在承载网上,由不同区域的服务器组成的分布式网络。将源站资源缓......
  • vue+leaflet示例:在线地图切换显示(附源码下载)
    demo源码运行环境以及配置运行环境:依赖Node安装环境,demo本地Node版本:14.19.1。运行工具:vscode或者其他工具。配置方式:下载demo源码,vscode打开,然后顺序执行以下命令:(1......
  • Go组件库总结之事件注册唤醒
    本篇文章我们用Go实现一个自定义事件注册并等待唤醒的机制,其中涉及到的链表操作可以参考上一篇文章。文章参考自:https://github.com/brewlin/net-protocol1.自定义唤醒事......
  • echarts渲染3d地图以及交互事件
    环境vue2导入安装npm包echartsecharts-glimport*asechartsfrom"echarts"import"echarts-gl"html<divid="map-container"></div>jsimportGeoZJfr......
  • LeetCode算法训练-贪心算法 455.分发饼干 376. 摆动序列 53. 最大子序和
    欢迎关注个人公众号:爱喝可可牛奶LeetCode算法训练-贪心算法455.分发饼干376.摆动序列53.最大子序和前置知识贪心算法核心是找局部最优解,通过局部最优推导出全局最......
  • 【Mybatis】【配置文件解析】【四】Mybatis源码解析-mappers的解析四(绑定Mapper、处理
    1 前言我们上节把我们mapper里的sql节点以及我们的增删改查都解析了,那么最后回来就剩下两块没看了,一块是我们的mapper跟我们的接口绑定,一块就是我们在解析的过程中......
  • js中阻止事件冒泡与阻止事件默认行为
    一、冒泡事件我们都知道冒泡就像水底气泡浮到水面这一过程。冒泡事件即是事件从最底层逐个经过上面一级级事件的过程,就是冒泡事件。那么如何有效的阻止冒泡事件的发生?其实在......
  • Apache HttpClient使用和源码分析
    在上文中分析了HttpURLConnection的用法,功能还是比较简单的,没有什么封装接下来看看ApacheHttpClient是如何封装httpClient的目录组成请求代码代码分析自定义拦截器和处......
  • 微信小程序中滚动事件deltaX值的含义
    以横向滚动为例,上代码:uniapp<scroll-viewclass="scroll-box"scroll-x@scroll="scroll":scroll-with-animation="true"enable-flex><viewclass="swiper-it......