Android 事件分发:为什么有时候会出现事件冲突?事件的顺序是如何的?出现事件冲突如何解决呢?比如为什么左右可以滑动,而上下却不行?

news2024/9/27 17:37:04

目录:

在这里插入图片描述


一、为什么要学习事件呢?

在这里插入图片描述

1.在开发复杂的应用时,经常需要处理复杂的用户交互逻辑。学习事件分发机制可以帮助你更好地控制事件的传递和处理流程,从而解决一些复杂的交互问题,如滑动冲突、点击穿透等。

2.面试需要:事件冲突的原因是什么?在开发过程中,可能会遇到一些与事件处理相关的问题,如事件没有被正确传递、事件被错误地拦截等。了解事件分发机制可以帮助你更快地定位问题所在,并找到解决方案。



二、事件是什么?事件是如何触发的?

事件(Event)是用户与应用程序界面(UI)交互时发生的动作或情况的抽象表示。这些事件可以是由用户通过触摸屏幕、按键、旋转设备等方式触发的,也可以是由系统产生的,比如系统状态变化(如屏幕旋转、电量变化)等。

当事件发生时,Android系统会将该事件发送给注册了该事件监听器的组件。然后,事件监听器中的相应方法会被调用,执行开发者定义的事件处理逻辑。



三、事件冲突是什么:举例说明,比如ViewPage和Recyclerview一起使用,会出现左右不能滑动或上下不能滑动的情况

遇到的问题

2.1 ViewPager的代码

class MyViewPager(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        return false
    }
}

2.2 RecyclerView的代码

class MyRecyclerView(context: Context, attrs: AttributeSet?) : RecyclerView(context, attrs) {

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {

        return super.dispatchTouchEvent(ev)
    }
}

出现了上下可以滑动,但是左右不可以。到底是什么原因导致的呢?

首先我们先了解一下事件的流程是怎么样的。



四、事件的大致流程

事件产生的原因,如下图:1个事件的发生,通过触摸手机屏幕——>导电——>传感器——>Linux——>Activity里面,再传到ViewGroup,最终再传到 View。

那么Activity里面,我们需要关注dispaatchToucheEvent方法。这里需要注意,ViewGrouper的dispaatchToucheEvent和View的dispaatchToucheEvent是做着不同的事情。后面我们会重点讲解

Android事件的处理流程是一个从硬件到软件、从底层到高层的复杂过程,涉及到底层硬件、Linux内核、Android框架以及最终的应用层。

这里我们只需要关注ViewGroup的dispaatchToucheEvent,以及View的dispaatchToucheEvent。也是下面我们需要重点讲解的



五、事件分发和事件处理是什么意思?

5.1 事件分发

事件分发,指在安卓系统中,如何处理和传递用户交互事件的一套机制,当一个点击事件发生后,系统需要将这个事件传递给一个具体的View去处理。这个事件传递的过程就是分发过程。这也是事件分发存在的目的,控件太多,他不知道具体是哪一个,所以他需要通过分发的方式,递归去找到这个事件的处理者

5.2 事件处理

事件处理是指当事件被传递到相应的组件后,该组件执行具体的操作来响应事件的过程。如处理触摸事件、点击事件等。

下面我们一起看看源码,了解一下他是如何进行事件分发的,如何事件处理的,从中找出如何解决事件冲突的方案。


六、事件处理源码分析

我们先看看事件处理的源码,从比较简单的先开始。事件分发留到后面。

比如我们现在给一个控件设置了点击事件和触摸事件。如下:

  btnEvent.setOnClickListener {
            Log.d(TAG, "onClick: ")
        }
  btnEvent.setOnTouchListener { view, event ->
      Log.d(TAG, "OnTouch: "+event.action)

      false
  }
  1. 那么你觉得哪个会先执行,事件的优先级是如何的,以及你可以如何去拦截事件的处理?日志如下:

在这里插入图片描述
可以看到onTouch方法的优先度会高,为什么呢?我们看看源码:

在这里插入图片描述
在dispatchTouchEvent方法中,如果控件注册了onTouchListener,则会先执行onTouch回调方法。

btnEvent.setOnClickListener {
            Log.d(TAG, "onClick: ")
        }
  btnEvent.setOnTouchListener { view, event ->
      Log.d(TAG, "OnTouch: "+event.action)

      true
  }

在这里插入图片描述

如果onTouch方法返回true,表示该事件已被消费,不会继续传递;如果返回false,则事件会继续传递给onTouchEvent方法。
onTouchEvent方法会处理包括ACTION_DOWN、ACTION_MOVE、ACTION_UP等在内的事件。当事件为ACTION_UP时,如果控件是可点击的(如按钮),则会调用performClick方法,进而触发onClick方法。

2.该事件已被消费,不会继续传递

在这里插入图片描述

  1. 我们看看继续传递的情况,看看onClick在哪里

在这里插入图片描述
因为点击事件是松开后触发的,所以我们要找到松开的地方。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

好的,事件处理的源码我们就看到这里,我们看看事件分发。


七、事件分发源码分析:我们会将源码划分为三块来讲

我们上面的例子,ViewPager+Recyclerview,Viewpager就是容器,而Recyclerview就是子view,下面我们就代入进去分析代码。

7.1 第一块源码:是否拦截子view,也就是,是否将事件分发给子view来出来,还是说自己处理


举例:如果我们的ViewPager的onInterceptTouchEvent方法返回了true,那么就拦截了子view,返回false,就不拦截子view,让他分发给子view处理事件。

在这里插入图片描述


7.2 第二块源码:不拦截子view,那么就开始遍历子view,看谁是否需要进行处理事件

如下是第二块代码,主要进行子view的事件分发,如果intercepted为false,那么就进来了。如果为true,那么第二块代码相当于什么都不执行,直接到第三块代码。

在这里插入图片描述


7.3 第三块源码:如果拦截了子view,那么就循环自己(ViewPager)是否需要处理事件,如果没有拦截,那么这里会看哪个子view需要进行事件的处理

在这里插入图片描述

7.4 具体案例

案例一:假如ViewPager里面直接返回true:RecyclerView的上下滑动事件,就不会得到执行。这是为什么?

class MyViewPager(context: Context) : ViewPager(context) {
    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
     
        return true
    }
}

其实我们看第一块源码我们就已经知道了答案,如果onInterceptTouchEvent返回true,就会进行拦截,那么就不会进行子View的事件分发。[效果如下gif]

在这里插入图片描述

案例二:为什么Viewpager返回false,RecyclerView不用设置,左右不能滑动了,但是上下能滑动,为什么呢?[效果如下gif]

在这里插入图片描述首先,我们看看源码,第一块肯定是通过了,我们看第二块。首先要拿到所有的子view,我们这里可能只有一个,但也有可能写多个,所以我们要拿到所有的子view,通过buildTouchDispatchChildList方法。
在这里插入图片描述拿到所有的子view以后,我们要进行倒序处理

在这里插入图片描述
为什么呢?我们知道,我们写三个控件,他们是层层叠加的,比如Viewpager+RecyclerView,那么Viewpager是在最底部,RecyclerView则是在上面,Viewpager就是0,RecyclerView就是1,所以我们要倒序过来,处理最上层view,看他是否需要处理事件,如果不处理,才到Viewpager。

如下代码,是判断是否点到了view的位置。不满足,肯定是不需要执行事件咯。所以我们设置了点击事件,但是没点击到,肯定就不会执行。
在这里插入图片描述如果ViewPager和RecyclerView都返回false,为什么viewpager的可以得到事件处理呢?因为他最终都会进行处理。一旦子view处理了事件,就不会在给父容器处理事件了。因为事件,始终只有一个人处理。

所以,如果ViewPager返回了false,而RecyclerView返回true,那么RecyclerView就能上下滑动,而ViewPager就不行了。

7.5 Down事件

Down事件其实就是我们上面说的这些代码。前面我们已经知道事件冲突的原因,因为事件,始终只有一个人处理。那么我们如何解决他呢,就需要我们了解move事件分发流程。

7.6 Move事件

我们先看看结论,看看是如何解决的。

class MyViewPager(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
    override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
        if (ev?.action == MotionEvent.ACTION_DOWN) {
            super.onInterceptTouchEvent(ev)
            return false
        }
        return true
    }
}
class MyRecyclerView(context: Context, attrs: AttributeSet?) : RecyclerView(context, attrs) {
    var mLastX:Float = 0f
    var mLastY:Float = 0f

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        var x = ev?.getX()
        var y = ev?.getY()

        when (ev?.action) {
            MotionEvent.ACTION_DOWN -> {
                requestDisallowInterceptTouchEvent(true)
            }

            MotionEvent.ACTION_MOVE -> {
                val deltaX = (x!! - mLastX).toInt()
                val deltaY = (y!! - mLastY).toInt()

                if (Math.abs(deltaX)>Math.abs(deltaY)){
                    requestDisallowInterceptTouchEvent(false)

                }
            }

            MotionEvent.ACTION_UP -> {}
            else -> {}
        }
        mLastX = x!!;
        mLastY = y!!;
        return super.dispatchTouchEvent(ev)
    }
}

首先,我们先了解一下,起始事件,是MotionEvent.ACTION_DOWN,也就是按下事件,大家都同意吧,那么这个时候,我们不要拦截子View,所以我们就返回了false.

if (ev?.action == MotionEvent.ACTION_DOWN) {
            super.onInterceptTouchEvent(ev)
            return false
        }

当滑动的事件的时候,那么可能是子View要处理,也可能是父View要处理,所以子View里面是这样写的

 when (ev?.action) {
            MotionEvent.ACTION_DOWN -> {
                requestDisallowInterceptTouchEvent(true)
            }

            MotionEvent.ACTION_MOVE -> {
                val deltaX = (x!! - mLastX).toInt()
                val deltaY = (y!! - mLastY).toInt()

                if (Math.abs(deltaX)>Math.abs(deltaY)){
                    requestDisallowInterceptTouchEvent(false)

                }
            }

            MotionEvent.ACTION_UP -> {}
            else -> {}
        }

requestDisallowInterceptTouchEvent(true)究竟是什么?如果子视图调用了这个方法,并且返回了 true,那么父视图将不会调用其 onInterceptTouchEvent(MotionEvent ev) 方法来尝试拦截这个触摸事件,也就是说子类一定会得到处理。除非后续的事件(如 ACTION_MOVE 或 ACTION_UP)改变了这个决策。所以我们在子View里面进行了处理,如果是左右方向,那么就可以让父容器去拦截,如果是上下方向,就不要让父容器拦截,让子view去进行处理。

为什么可以这样呢,因为Move事件不是触发一次,而是多次。

我们进入requestDisallowInterceptTouchEvent方法里面去看看。
在这里插入图片描述
这里设置了条件以后,我们可以到第一块代码这看看,因为第一块代码是决定是否分发事件给子View。
在这里插入图片描述

强行使其给子View进行事件的分发。所以我们需要requestDisallowInterceptTouchEvent分发来进行处理。

好了,那么事件的介绍就分享到这里了~

八、学习总结

事件分发的源码第一次看比较复杂,需要反复观看,才能理解,并且结合控件案例去测试,效果会更好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2089341.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

NLP笔记:BLEU

1 介绍 bleu是一种文本评估算法,它是用来评估机器翻译跟专业人工翻译之间的对应关系核心思想就是机器翻译越接近专业人工翻译,质量就越好,经过bleu算法得出的分数可以作为机器翻译质量的一个指标 2 BLEU原理 2.1 N-gram BLEU采用了N-gram…

NLP(三):词向量

自然语言处理,处理的是自然的需要,通过分词后得到我们想要的词,但是不可能直接把这种自然语言传递给计算机来理解。这时候就有一个概念叫词向量,用来表示词的特征向量或表征。 一,词向量的表示 词向量的表示主要有两…

C——四种排序方法

这一篇文章我将要详细讲解四种排序方法 1.冒泡排序 冒泡排序是我们首先接触的排序方法&#xff0c;他通过两次循环完成。 /*冒泡排序*/&#xff08;升序&#xff09; void maopao(int *a,int n) {int i;for(i 0; i < n-1; i){for (int j 0; j < n - 1 - i; j){if (a[…

mars3D使用 POI 查询、限定范围

mars3D使用 一、mars3D中使用 geocoder 进行 POI 查询二、限定范围1.初始化时渲染2.重新渲染 总结 一、mars3D中使用 geocoder 进行 POI 查询 在json文件或者自己的mapOptions中配置token "token":{"tianditu":"e5c3984ced09bc1f55e8e1107fdc5a6b&q…

论文速览【LLM-agent】—— 【ReAct】Synergizing Reasoning and Acting in Language Models

文章链接&#xff1a;ReAct: Synergizing Reasoning and Acting in Language Models发表&#xff1a;ICLR 2023领域&#xff1a;LLM agent 摘要&#xff1a;尽管大型语言模型&#xff08;LLMs&#xff09;在语言理解和交互式决策任务中展示了令人印象深刻的能力&#xff0c;但它…

正弦波振荡器工作原理及频率稳定性条件

晶发电子专注17年晶振生产,晶振产品包括石英晶体谐振器、振荡器、贴片晶振、32.768Khz时钟晶振、有源晶振、无源晶振等&#xff0c;产品性能稳定,品质过硬,价格好,交期快.国产晶振品牌您值得信赖的晶振供应商。 正弦波振荡器是一种能够自动将直流电转换为特定频率和振幅的正弦交…

解析云原生架构中两大核心原则

1.云原生架构是什么 云原生架构是一种设计和构建应用程序的现代方法&#xff0c;以微服务、容器化、持续集成和持续部署&#xff08;CI/CD&#xff09;等技术为基础&#xff0c;使应用能够在云环境中动态运行。云原生架构强调解耦合、弹性和自动化&#xff0c;开发团队在独立的…

【高级编程】万字整理集合框架 迭代器 泛型(含方法案例)

文章目录 集合框架集合接口集合类ArrayListLinkedListHashSet 迭代器 IteratorMap 接口泛型Collections 工具类 集合框架 如果并不知道程序运行时会需要多少对象&#xff0c;或者需要更复杂方式存储对象——可以使用Java集合框架 Java集合框架提供了一套性能优良、使用方便的…

@EqualsAndHashCode注解使用

一&#xff0c;EqualsAndHashCode注解来自于Lombok EqualsAndHashCode 是 Lombok 库提供的一个注解&#xff0c;用于自动生成 equals 和 hashCode 方法。这两个方法在 Java 中非常重要&#xff0c;特别是在集合框架中使用时&#xff0c;它们确保了对象的正确比较和哈希值的一致…

YOLOv8改进 | 注意力篇 | YOLOv8引入LSK注意力机制

1. LSK介绍 1.1 摘要: 最近关于遥感目标检测的研究主要集中在改进定向边界框的表示上,但忽略了遥感场景中呈现的独特先验知识。 这种先验知识可能很有用,因为在没有参考足够远距离上下文的情况下,可能会错误地检测微小的遥感物体,并且不同类型物体所需的远距离上下文可能…

HarmonyOS开发实战( Beta5版)优化实践/合理使用缓存提升性能

简介 随着应用功能的日益丰富与复杂化&#xff0c;数据加载效率成为了衡量应用性能的重要指标。不合理的加载策略往往导致用户面临长时间的等待&#xff0c;这不仅损害了用户体验&#xff0c;还可能引发用户流失。因此&#xff0c;合理运用缓存技术变得尤为重要。 系统提供了P…

uniapp组件用法

一. 什么是组件,有什么好处? 在uni-app中&#xff0c;组件是构成应用的基本单位&#xff0c;它们是用来定义用户界面的一部分&#xff0c;并且通常包含了视图和逻辑。组件的设计使得开发者能够以声明式的方式构建应用界面&#xff0c;并且通过组件化的开发方式来提高代码的复…

损失函数、成本函数cost 、最大似然估计

一、损失函数 什么是损失函数&#xff1f; 【深度学习】一文读懂机器学习常用损失函数&#xff08;Loss Function&#xff09;-腾讯云开发者社区-腾讯云 损失函数&#xff08;loss function&#xff09;是用来估量模型的预测值f(x)与真实值Y的不一致程度&#xff0c;它是一个…

Python 新手必看:如何用 unittest 写出高质量代码?

文末赠免费精品编程资料~~ 在 Python中 &#xff0c;unittest 模块是进行单元测试的强大工具。无论你是初学者还是有经验的开发者&#xff0c;单元测试都是确保代码质量的重要一环。而 unittest 模块就是让这一过程变得简单、快捷的利器。 什么是单元测试&#xff1f; 在进入…

浩瀚麦克风怎么样?西圣、罗德、神牛领夹麦克风全网巅峰PK测评

​一台优质专业的无线领夹麦克风能够清晰、稳定地收录声音&#xff0c;提升音频录制质量。而劣质的无线领夹麦克风则可能出现声音不清晰、信号不稳定、续航短等各种问题。作为一名资深的数码测评师&#xff0c;我已经测评过了好几十款无线领夹麦克风&#xff0c;今天将从麦克风…

Python应用指南:获取高德地铁站点数据(单城市版)

书接上文&#xff0c;上篇文章是一次性下载全国所以城市的地铁站点数据&#xff0c;但是可视化的过程需要手动把换乘站给一个个复制出来分配到其他各个经过的线路&#xff0c;还需要核对站点顺序不能出错&#xff0c;如果只需要单个城市的数据呢&#xff1f;另外能不能直接生成…

【复杂系统系列(初级)】自动调节动态平衡模型——生物体的稳态机制

【通俗理解】自动调节动态平衡模型——生物体的稳态机制 关键词提炼 #自动调节 #动态平衡 #生物体稳态 #反馈机制 #体温调节 #微分方程模型 第一节&#xff1a;自动调节动态平衡模型的类比与核心概念 1.1 自动调节动态平衡模型的类比 自动调节动态平衡模型可以被视为生物体…

grpc-spring 通信(监控视频传输)

先看效果 这是微软相机&#xff0c;22ms延迟 &#xff08;不走网络存粹寄存器和内存的通信&#xff09;这是程序抓取摄像头然后传给client&#xff0c;client的java窗口展示的&#xff0c;延时也是22ms&#xff08;对了localhost好像也不走网络吧&#xff09; 几个点 1.openc…

大功率舞台灯调光调色方案 | 支持深度调光,多路输出调光 36V/48V/60V FP7126

在舞台演出中&#xff0c;灯光扮演着非常重要的角色&#xff0c;它不仅可以烘托氛围&#xff0c;营造氛围&#xff0c;更能够为表演者增添光彩&#xff0c;塑造形象。在博物馆场所中&#xff0c;突出展品细节。根据灯光用途和适用类型&#xff0c;舞台灯可以细分为聚光灯、泛光…

foundation model

目录 多模态预训练模型 BLIP LLM GPT3 InstructGPT FLAN chain of thought ToolFormer QWEN Llama3 VLM QWen-VL VideoChat Video-ChatGPT 应用 DriveVLM PlanAgent 多模态预训练模型 CLIP BLIP 《Bootstrapping Language-Image Pre-training for Unified Visi…