面试题:设置view点击事件不回调的几种方式和原理

如何设置view 点击事件不回调,如何实现?有什么区别?

setEnabled(false)

这个方案用于设置view是否可以响应用户的其他交互事件如触摸,轨迹球等。

setClickable(false)

这个方法用于设置view是否可以响应用户的点击事件。

setOnTouchListener{ return true}

设置监听,并且表示消费事件。

直接重写onTouchEvent 不要super相关逻辑
override fun onTouchEvent(event: MotionEvent?): Boolean {return true
}
直接重写 dispatchTouchEvent 不要super 相关逻辑
override fun dispatchTouchEvent(event: MotionEvent?): Boolean {return true
}

dispatchTouchEvent 相关

事件的责任链模式中,在view层只有两个:

  • onTouchEvent,返回true 表示消费事件
  • dispatchTouchEvent false 表示不分发,自己消费

但是view 层对这两个函数有默认实现。所以我们自定义view的时候,很少全部都放弃super 相关逻辑,这很毒瘤。而且dispatchTouchEvent 作为事件的分发,这个一般不会重写。最多是处理onTouchEvent。

但是setOnTouchListener 的分发则是在dispatchTouchEvent 函数中。在dispatchTouchEvent这里:

if (li != null && li.mOnTouchListener != null&& (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {result = true;
}
​
if (!result && onTouchEvent(event)) {result = true;
}

当我们onTouch 返回了true,则导致下面if 中前面的条件 !result=false,那么onTouchEvent函数就没有调用了,这也是setOnTouchListener 优先级高于 的原因,所以我们这里解决了为什么 setOnTouchListener{ return true}直接重写 dispatchTouchEvent 不要super 相关逻辑点击事件不回调的问题。

在来看一个问题 setEnabled(false) 是可以管控到触摸事件的,我们再来dispatchTouchEvent的代码:

 if (onFilterTouchEventForSecurity(event)) {// 上面分发代码在这个里面。}

onFilterTouchEventForSecurity:

public boolean onFilterTouchEventForSecurity(MotionEvent event) {//noinspection RedundantIfStatementif ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {// Window is obscured, drop this touch.return false;}return true;
}

结合 setEnabled() 源码中的部分代码:

setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);

可以看到,对于mViewFlags 赋值成了DISABLED,就变成了:

static final int FILTER_TOUCHES_WHEN_OBSCURED = 0x00000400;
static final int DISABLED = 0x00000020;
boolean result= (DISABLED&FILTER_TOUCHES_WHEN_OBSCURED)!=0;

导致onFilterTouchEventForSecurity 直接返回false,所以后续的 mOnTouchListener.onTouchonTouchEvent(event) 都没有被执行了。

onTouchEvent 相关

通过上面的知识点,我们就只剩下setClickable 没有开始找为什么了,如果其他的都正确的话,那么我们事件就会传递到onTouchEvent 中。

我们先来看serClickable 源码:

public void setClickable(boolean clickable) {setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
}

很单纯,设置了一个Flag =CLICKABLE。在OnTouchEvent 中:

final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE|| (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)|| (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;

clickable=false ,就导致if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) 这个循环根本就没有进去。所以说setClickable(false) 最终影响到了 判断的执行。

我们知道点击事件回调是当action=MotionEvent.ACTION_UP的时候触发:

  • performClickInternal();
  • performClick():
public boolean performClick() {notifyAutofillManagerOnClick();
​final boolean result;final ListenerInfo li = mListenerInfo;if (li != null && li.mOnClickListener != null) {playSoundEffect(SoundEffectConstants.CLICK);li.mOnClickListener.onClick(this);result = true;} else {result = false;}sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
​notifyEnterOrExitForAutoFillIfNeeded(true);
​return result;
}

可以看到。performClick实现内部调用了li.mOnClickListener.onClick(this);而mOnClickListener就是我们设置的点击事件。通过这个逻辑,那么 直接重写onTouchEvent 不要super相关逻辑 也可以实现点击事件不回调了。

setClickable(false) 无效

可以看到下面的代码:

isClickable=false
setOnClickListener {LogUtils.e("setOnClickListener")
}

我们先设置了clickable,又设置了点击事件。但是点击事件可以响应,为什么呢?我们来看下设置点击事件的源码就知道了:

public void setOnClickListener(@Nullable OnClickListener l) {if (!isClickable()) {setClickable(true);}getListenerInfo().mOnClickListener = l;
}

非常单纯的代码,如果是不可点击,那就设置为可以点击。所以 setClickable(false)得写到设置点击事件之后。

总结

其实,这个逻辑还是蛮简单的,主要是要点一下代码。最终汇总下:

  • view 的dispatchTouchEvent 有默认实现,当重写后,放弃super,那么直接影响了点击事件和触摸事件等事件的分发,滚动也被影响了。所以点击事件回调就无法触发,因为没有代码调用到点击事件。
  • setEnabled 将flag 修改成了DISABLED,导致onFilterTouchEventForSecurity返回了false,所以触摸事件回调和onTouchEvent 事件都没有调用到。而点击事件回调在onTouchEvent 里面。
  • setOnTouchListener{ return true} 会导致onTouchEvent 不会被调用,是这么屏蔽的点击事件回调。
  • setClickable(false) 也是更改的flag=CLICKABLE,会导致onTouchEvent 中的clickable 等于false,所以事件还没有分发就结束了。
  • 重写onTouchEvent,不要super,这种思路还是直接放弃了源码的实现,所以函数也没有地方调用。

事件分发和绘制原理,还是得懂一下,毕竟现在各个系统打架,懂了,跨平台方案可能学习得快一点吧。

Android 学习笔录

Android 往年面试题锦:https://qr18.cn/CKV8OZ
2023年最新Android 面试题集:https://qr18.cn/CgxrRy
Android 车载开发岗位面试习题:https://qr18.cn/FTlyCJ
音视频面试题锦:https://qr18.cn/AcV6Ap
Android 性能优化篇:https://qr18.cn/FVlo89
Android Framework底层原理篇:https://qr18.cn/AQpN4J
Android 车载篇:https://qr18.cn/F05ZCM
Android 逆向安全学习笔记:https://qr18.cn/CQ5TcL
Android 音视频篇:https://qr18.cn/Ei3VPD
Jetpack全家桶篇(内含Compose):https://qr18.cn/A0gajp
OkHttp 源码解析笔记:https://qr18.cn/Cw0pBD
Kotlin 篇:https://qr18.cn/CdjtAF
Gradle 篇:https://qr18.cn/DzrmMB
Flutter 篇:https://qr18.cn/DIvKma
Android 八大知识体:https://qr18.cn/CyxarU
Android 核心笔记:https://qr21.cn/CaZQLo

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

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

相关文章

技术分享 | 如何写好测试用例?

对于软件测试工程师来说,设计测试用例和提交缺陷报告是最基本的职业技能。是非常重要的部分。一个好的测试用例能够指示测试人员如何对软件进行测试。在这篇文章中,我们将介绍测试用例设计常用的几种方法,以及如何编写高效的测试用例。 ## 一…

vue和uni-app的递归组件排坑

有这样一个数组数据,实际可能有很多级。 tree: [{id: 1,name: 1,children: [{ id: 2, name: 1-1, children: [{id: 7, name: 1-1-1,children: []}]},{ id: 3, name: 1-2 }]},{id: 4,name: 2,children: [{ id: 5, name: 2-1 },{ id: 6, name: 2-2 }]} ]要渲染为下面…

java算法学习索引之二叉树问题

一 分别用递归和非递归方式实现二叉树先序、中序和后序遍历 用递归和非递归方式,分别按照二叉树先序、中序和后序打印所有的节点。我们约定:先序遍历顺序为根、左、右;中序遍历顺序为左、根、右;后序遍历顺序为左、右、根。 递…

春秋云境靶场CVE-2022-30887漏洞复现(任意文件上传漏洞)

文章目录 前言一、CVE-2022-30887描述和介绍二、CVE-2021-41402漏洞复现1、信息收集2、找可能可以进行任意php代码执行的地方3、漏洞利用找flag 总结 前言 此文章只用于学习和反思巩固渗透测试知识,禁止用于做非法攻击。注意靶场是可以练习的平台,不能随…

C语言中的指针(上)

目录 一、基本概念 1.变量的存储空间 2.定义指针 3.引用与解引用 二、指针的算术运算、类型以及通用指针 1.指针的算数运算 2.指针类型以及通用型指针 三、指向指针的指针(pointers to pointers) 四、函数传值以及传引用 1.局部变量 2.从存储地…

IOS输入框聚焦会把内容区域顶起

前几天做了一个类似qq布局的h5的聊天界面,输入框固定在最底下。本来初始情况会有默认的两条聊天记录,但是当点击底部的输入框时,输入框聚焦,弹起键盘,然后整个界面就被顶上去了,然后那两条默认的聊天记录也…

gitlab环境准备

1.准备环境 gitlab只支持linux系统,本人在虚拟机下使用Ubuntu作为操作系统,gitlab镜像要使用和操作系统版本对应的版本,(ubuntu18.04,gitlab-ce_13.2.3-ce.0_amd64 .deb) book100ask:/$ lsb_release -a No LSB modules are available. Dist…

机器学习二元分类 二元交叉熵 二元分类例子

二元交叉熵损失函数 深度学习中的二元分类损失函数通常采用二元交叉熵(Binary Cross-Entropy)作为损失函数。 二元交叉熵损失函数的基本公式是: L(y, y_pred) -y * log(y_pred) - (1 - y) * log(1 - y_pred)其中,y是真实标签&…

【C++11】右值引用使用详解

系列文章目录 C11新特性使用详解-持续更新 文章目录 系列文章目录前言一、关联特性1.1 左值/右值 二、使用方法2.1 获得右值引用2.2 对象移动方法2.2.1 移动构造函数/移动赋值运算符2.2.2 标记为noexcept2.2.3 使移动源对象进入是可析构状态 三、使用场景3.1 移动语义3.1 完美…

中贝通信-603220 三季报分析(20231120)

中贝通信-603220 基本情况 公司名称:中贝通信集团股份有限公司 A股简称:中贝通信 成立日期:1999-12-29 上市日期:2018-11-15 所属行业:软件和信息技术服务业 周期性:1 主营业务:通信网络技术服务…

Qt ListWidget

先创建QListWidgetItem: QListWidgetItem* pListItem1 new QListWidgetItem(QIcon(":/resources/editor.png"),u8"editor");QListWidgetItem* pListItem2 new QListWidgetItem(QIcon(":/resources/env.png"),u8"env");Q…

通信网络安全防护定级备案流程介绍(附流程图)

通信网络安全防护定级备案是拥有增值电信业务经营许可证并且有开展电信业务的企业要做的一件事情。刚接触这块的家人们在填报操作的时候可能对具体通信网络安全防护定级备案流程还不是很清楚,所以就给大家画张具体的流程图吧,可以更加直观的了解。 通信…

go语言学习-go环境安装

1、安装Go 1.1 下载安装 go官网 找对应电脑的版本进行安装即可。 点击安装包,直接下一步下一步即可,安装目录可以自行设置一下。 1.2 验证 windows通过cmd验证。 linux或者mac可以通过自带终端执行测试。 2、配置环境变量 2.1 windows 找到系统…

HarmonyOS开发(四):UIAbility组件

1、UIAbility概述 UIAbility 一种包含用户界面的应用组件用于与用户进行交互系统调度的单元为应用提供窗口在其中绘制界同 注:每一个UIAbility实例,都对应一个最近任务列表中的任务。 一个应用可以有一个UIAbility也可以有多个UIAbility。 如一般的…

Docker 启动alpine镜像中可执行程序文件遇到 not found

## 1. 问题: docker alpine镜像中遇到 sh: xxx: not found 例如: 在容器内/app/目录下放置了可执行文件abc,启动时提示not found /app/startup.sh: line 5: ./abc : not found ## 2. 原因 由于alpine镜像使用的是musl libc而不是gnu libc&am…

深度学习YOLO安检管制物品识别与检测 - python opencv 计算机竞赛

文章目录 0 前言1 课题背景2 实现效果3 卷积神经网络4 Yolov55 模型训练6 实现效果7 最后 0 前言 🔥 优质竞赛项目系列,今天要分享的是 🚩 **基于深度学习YOLO安检管制误判识别与检测 ** 该项目较为新颖,适合作为竞赛课题方向&…

【论文阅读】SPARK:针对视觉跟踪的空间感知在线增量攻击

SPARK: Spatial-Aware Online Incremental Attack Against Visual Tracking introduction 在本文中,我们确定了视觉跟踪对抗性攻击的一个新任务:在线生成难以察觉的扰动,误导跟踪器沿着不正确的(无目标攻击,UA&#x…

设计模式--模板方法外观模式

模板方法模式 场景:需使用代码方式实现,考完试后,将各个学生的试卷及答案誊抄一份。 假如有两个学生的试卷誊抄完毕. // 学生A public class TestPaperA {// 试题1public void testQuestion1() {System.out.println("问题一:XXXXXXXX…

【C++11】Lambda表达式使用详解

系列文章目录 C11新特性使用详解-持续更新 文章目录 系列文章目录简介一、特点二、语法结构三、实例1.排序2.容器赋值3.传参 简介 Lambda表达式是一种用于创建匿名函数的语法结构。它可以在需要函数对象的地方使用,而无需显式定义一个命名函数。Lambda表达式在C中…