当前位置: 首页 > news >正文

安卓触摸事件的传递

setOnTouchListener()返回值的副作用(触摸事件是否继续往下或往后传递)如下:

返回值效果是否往下层view传递是否往当前view的后续监听传递
true该pointer离开屏幕前的后续所有触摸事件都会传递给该TouchListener
false该pointer离开屏幕前的后续所有触摸事件都不会再传递给该TouchListener

注:

  1. 如果view设置了setOnClickListenersetOnLongClickListener,效果等同于在setOnTouchListener()执行完setOnClickListenersetOnLongClickListener的业务逻辑后返回true
  2. 并非所有view都允许触摸事件往下传递,如Button及其子类就不允许触摸事件向下传递,应该是默认实现了setOnClickListener
  3. 触摸事件执行先后顺序为setOnTouchListener -> setOnLongClickListener -> setOnClickListener

触摸事件的传递可以用以下代码理解:

package com.example.study.controller;import java.lang.reflect.Field;
import java.text.SimpleDateFormat;
import java.util.*;/*** 安卓处理触摸事件示意(为方便理解,假设只有一个手指pointer触摸屏幕)*/
public class TouchEventProcess {// 长按的时间private static final long LONG_CLICK_TIME_MILLIS = 500L;private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");private boolean touch = false;private boolean longClick = false;private Timer timer;private ViewListener viewListener;private List<View> list = new ArrayList<>();/*** 在这里设置触摸监听*/public TouchEventProcess() {viewListener = new ViewListener();viewListener.setOnTouchListener((view, event) -> {System.out.println(DATE_FORMAT.format(new Date()) + " process touch event:" + MotionEvent.getEventName(event.actionMasked));return false;});viewListener.setOnLongClickListener(view -> {System.out.println(DATE_FORMAT.format(new Date()) + " process long click event");return false;});viewListener.setOnClickListener(view -> {System.out.println(DATE_FORMAT.format(new Date()) + " process click event");});}/*** 多个view** @param event*/public void processTouchEvent(MotionEvent event) {for (View view : list) {if (processTouchEventInView(view, event)) {return;}System.out.println("=====touc event trans to next view=====");}}public boolean processTouchEventInView(View view, MotionEvent event) {// 如果当前view最终返回的是false,不再响应当前pointer的触摸事件if (!viewListener.hasAnyListener()) {reset(event, "no process");return false;}if (viewListener.getOnTouchListener() != null) {touch = viewListener.getOnTouchListener().onTouch(view, event);}if (touch) {reset(event, "process touch");return true;}if (viewListener.getOnLongClickListener() != null) {if (event.actionMasked == MotionEvent.ACTION_DOWN) {timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {longClick = viewListener.getOnLongClickListener().onLongClick(view);timer.cancel();}}, LONG_CLICK_TIME_MILLIS);}}if (longClick) {reset(event, "process long click");return viewListener.hasClickListener();}if (viewListener.getOnClickListener() != null) {if (event.actionMasked == MotionEvent.ACTION_UP) {viewListener.getOnClickListener().onClick(view);}}reset(event, "process end");return viewListener.hasClickListener();}private void reset(MotionEvent event, String msg) {// 抬起手指pointer时重置if (event.actionMasked != MotionEvent.ACTION_UP) {return;}touch = false;longClick = false;if (timer != null) {timer.cancel();}System.out.println(DATE_FORMAT.format(new Date()) + " reset by " + msg);}public interface OnTouchListener extends Listener {boolean onTouch(View view, MotionEvent event);}public interface OnLongClickListener extends Listener {boolean onLongClick(View view);}public interface OnClickListener extends Listener {void onClick(View view);}public interface Listener {}static class ViewListener {private OnTouchListener onTouchListener;private OnLongClickListener onLongClickListener;private OnClickListener onClickListener;public OnTouchListener getOnTouchListener() {return onTouchListener;}public void setOnTouchListener(OnTouchListener onTouchListener) {this.onTouchListener = onTouchListener;}public OnLongClickListener getOnLongClickListener() {return onLongClickListener;}public void setOnLongClickListener(OnLongClickListener onLongClickListener) {this.onLongClickListener = onLongClickListener;}public OnClickListener getOnClickListener() {return onClickListener;}public void setOnClickListener(OnClickListener onClickListener) {this.onClickListener = onClickListener;}public boolean hasAnyListener() {return onTouchListener != null || onLongClickListener != null || onClickListener != null;}public boolean hasClickListener() {return onLongClickListener != null || onClickListener != null;}}static class View {}static class MotionEvent {public static int ACTION_DOWN = 0;public static int ACTION_MOVE = 1;public static int ACTION_UP = 2;int actionMasked;public MotionEvent(int actionMasked) {this.actionMasked = actionMasked;}public static String getEventName(int actionMasked) {for (Field field : MotionEvent.class.getFields()) {try {if ((int) field.get(MotionEvent.class) == actionMasked) {return field.getName();}} catch (IllegalAccessException e) {throw new RuntimeException(e);}}return "unknow";}}public static void main(String[] args) {TouchEventProcess touchEventProcess = new TouchEventProcess();touchEventProcess.list.add(new View());touchEventProcess.list.add(new View());touchEventProcess.list.add(new View());touchEventProcess.processTouchEvent(new MotionEvent(MotionEvent.ACTION_DOWN));touchEventProcess.processTouchEvent(new MotionEvent(MotionEvent.ACTION_MOVE));long click = 100L;long longClick = 1000L;Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {touchEventProcess.processTouchEvent(new MotionEvent(MotionEvent.ACTION_UP));timer.cancel();}}, longClick); // 调整delay即可切换短按长按}
}

验证代码

可用以下代码验证触摸事件的传递:

布局文件touch_event_test.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/touch_test_0"android:layout_width="match_parent"android:layout_height="match_parent"android:background="#F0F0F0"android:orientation="vertical"><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/white"android:padding="5dp"android:text="return true"android:textSize="32sp"android:textStyle="bold" /><!--第一层--><LinearLayoutandroid:id="@+id/touch_test_1_3"android:layout_width="300dp"android:layout_height="300dp"android:background="#7A7374"><!--第二层--><LinearLayoutandroid:id="@+id/touch_test_1_2"android:layout_width="200dp"android:layout_height="200dp"android:background="#1BA784"><!--第三层--><TextViewandroid:id="@+id/touch_test_1_1"android:layout_width="100dp"android:layout_height="100dp"android:background="#EB507E"android:gravity="bottom|right"android:padding="5dp"android:text="1_1"android:textSize="16sp"android:textStyle="bold" /><TextViewandroid:layout_width="100dp"android:layout_height="100dp"android:layout_gravity="bottom|right"android:gravity="bottom|right"android:padding="5dp"android:text="1_2"android:textSize="16sp"android:textStyle="bold" /></LinearLayout><TextViewandroid:layout_width="100dp"android:layout_height="100dp"android:layout_gravity="bottom|right"android:gravity="bottom|right"android:padding="5dp"android:text="1_3"android:textSize="16sp"android:textStyle="bold" /></LinearLayout><!--分割线--><ImageViewandroid:layout_width="match_parent"android:layout_height="1dp"android:layout_marginTop="5dp"android:layout_marginBottom="5dp"android:background="#000000" /><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/white"android:padding="5dp"android:text="return false"android:textSize="32sp"android:textStyle="bold" /><!--第一层--><LinearLayoutandroid:id="@+id/touch_test_2_3"android:layout_width="300dp"android:layout_height="300dp"android:background="#7A7374"><!--第二层--><LinearLayoutandroid:id="@+id/touch_test_2_2"android:layout_width="200dp"android:layout_height="200dp"android:background="#1BA784"><!--第三层--><TextViewandroid:id="@+id/touch_test_2_1"android:layout_width="100dp"android:layout_height="100dp"android:background="#EB507E"android:gravity="bottom|right"android:padding="5dp"android:text="2_1"android:textSize="16sp"android:textStyle="bold" /><TextViewandroid:layout_width="100dp"android:layout_height="100dp"android:layout_gravity="bottom|right"android:gravity="bottom|right"android:padding="5dp"android:text="2_2"android:textSize="16sp"android:textStyle="bold" /></LinearLayout><TextViewandroid:layout_width="100dp"android:layout_height="100dp"android:layout_gravity="bottom|right"android:gravity="bottom|right"android:padding="5dp"android:text="2_3"android:textSize="16sp"android:textStyle="bold" /></LinearLayout>
</LinearLayout>

TouchEventTestActivity.java

package org.tao.hetools.activities;import android.annotation.SuppressLint;
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Toast;import androidx.activity.ComponentActivity;
import androidx.annotation.Nullable;import org.tao.hetools.R;public class TouchEventTestActivity extends ComponentActivity {private static final String TAG = "TouchEventTestActivity";@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.touch_event_test);initView();}@SuppressLint("ClickableViewAccessibility")private void initView() {View _0 = findViewById(R.id.touch_test_0);View _1_1 = findViewById(R.id.touch_test_1_1);View _1_2 = findViewById(R.id.touch_test_1_2);View _1_3 = findViewById(R.id.touch_test_1_3);View _2_1 = findViewById(R.id.touch_test_2_1);View _2_2 = findViewById(R.id.touch_test_2_2);View _2_3 = findViewById(R.id.touch_test_2_3);_0.setOnTouchListener((view, event) -> {showToast(event, "_0");return true;});// return true_1_1.setOnTouchListener((view, event) -> {showToast(event, "_1_1");return true;});// 设置click longClick事件会对触摸事件在view之间的传递有影响,下同// _1_1.setOnClickListener(view -> Toast.makeText(this, "_1_1 clicked", Toast.LENGTH_SHORT).show());// _1_1.setOnLongClickListener(view -> {//     Toast.makeText(this, "_1_1 long clicked", Toast.LENGTH_SHORT).show();//     return true;// });_1_2.setOnTouchListener((view, event) -> {showToast(event, "_1_2");return true;});_1_3.setOnTouchListener((view, event) -> {showToast(event, "_1_3");return true;});// return false_2_1.setOnTouchListener((view, event) -> {showToast(event, "_2_1");return false;});// _2_1.setOnClickListener(view -> Toast.makeText(this, "_2_1 clicked", Toast.LENGTH_SHORT).show());// _2_1.setOnLongClickListener(view -> {//     Toast.makeText(this, "_2_1 long clicked", Toast.LENGTH_SHORT).show();//     return true;// });_2_2.setOnTouchListener((view, event) -> {showToast(event, "_2_2");return false;});_2_3.setOnTouchListener((view, event) -> {showToast(event, "_2_3");return false;});}private void showToast(MotionEvent event, String msg) {Log.i(TAG, msg + " " + event.getActionMasked());switch (event.getActionMasked()) {case MotionEvent.ACTION_DOWN -> Toast.makeText(this, msg + " press down", Toast.LENGTH_SHORT).show();case MotionEvent.ACTION_UP -> Toast.makeText(this, msg + " press up", Toast.LENGTH_SHORT).show();}}
}

相关文章:

安卓触摸事件的传递

setOnTouchListener()返回值的副作用&#xff08;触摸事件是否继续往下或往后传递&#xff09;如下&#xff1a; 返回值效果是否往下层view传递是否往当前view的后续监听传递true该pointer离开屏幕前的后续所有触摸事件都会传递给该TouchListener否否false该pointer离开屏幕前…...

idea项目导入gitee 码云

1、安装gitee插件 IDEA 码云插件已由 gitosc 更名为 gitee。 1 在码云平台帮助文档http://git.mydoc.io/?t153739上介绍的很清楚&#xff0c;推荐前两种方法&#xff0c; 搜索码云插件的时候记得名字是gitee&#xff0c;gitosc已经搜不到了。 2、使用码云托管项目 如果之…...

典型常见的基于知识蒸馏的目标检测方法总结三

来源&#xff1a;Google学术2023-2024的顶会顶刊论文 NeurIPS 2022&#xff1a;Towards Efficient 3D Object Detection with Knowledge Distillation 为3D目标检测提出了一种知识蒸馏的Benchmark范式&#xff0c;包含feature的KD&#xff0c;Logit的cls和reg的KD&#xff0c…...

端口被占用

端口8080被占用 哈哈哈&#xff0c;我是因为后端项目跑错了&#xff0c;两个项目后端名称太像了&#xff1b; &#xff08;1&#xff09;netstat -aon | findstr 8080&#xff0c;找到占用8080端口的进程号&#xff0c;获取对应的进程号pid&#xff1b; &#xff08;2&#…...

Javascript知识框架图(待完善)

以下是一个清晰且详细的 JavaScript 知识框架&#xff0c;涵盖基础知识到高级概念&#xff0c;适合学习和参考&#xff1a; JavaScript 知识框架 1. 基础知识 数据类型 原始类型&#xff1a;Number&#xff0c;String&#xff0c;Boolean&#xff0c;Null&#xff0c;Undefin…...

清华大学Python包镜像站点

清华大学提供了一个Python包镜像站点&#xff0c;其中包括了许多常用的Python包。使用这个镜像站点可以提高下载Python包时的速度&#xff0c;因为包已经存储在国内的服务器上&#xff0c;从而减少了网络延迟。 要使用清华的pip镜像&#xff0c;你可以在pip命令中指定-i参数来…...

逆境清醒文章总目录表

逆境清醒文章总目录表 零、时光宝盒&#x1f33b; &#xff08;https://blog.csdn.net/weixin_69553582 逆境清醒&#xff09; 《你的答案》歌曲原唱&#xff1a;阿冗&#xff0c;填 词&#xff1a;林晨阳、刘涛&#xff0c;谱曲&#xff1a;刘涛 也许世界就这样&#xff0c…...

LeetCode算法题——移除元素

题目描述 给你一个数组 nums 和一个值 val&#xff0c;你需要原地移除所有数值等于 val 的元素。元素的顺序可能发生改变。然后返回 nums 中与 val 不同的元素的数量。 假设 nums 中不等于 val 的元素数量为 k&#xff0c;要通过此题&#xff0c;您需要执行以下操作&#xff1…...

常见的中间件漏洞

1.Tomcat Tomcat介绍 tomcat是⼀个开源而且免费的jsp服务器&#xff0c;默认端口 : 8080&#xff0c;属于轻量级应⽤服务器。它可以实现 JavaWeb程序的装载&#xff0c;是配置JSP&#xff08;Java Server Page&#xff09;和JAVA系统必备的⼀款环境。 在历史上也披露出来了很…...

IPv6的过度技术

如何界定手动与自动&#xff1f;  主要是隧道目标地址能否自动获取 &#x1f46f;1. 双栈 必须支持IPv4和IPv6协议  链接双栈网络的接口必须同时配置v4和v6地址  路由器能够根据二层标记识别协议&#xff0c;type&#xff1a;0x0800代表IPV4&#xff0c;type&#xff1a;0x…...

Python用K-Means均值聚类、LRFMC模型对航空公司客户数据价值可视化分析指标应用|数据分享...

全文链接&#xff1a;https://tecdat.cn/?p38708 分析师&#xff1a;Yuling Fang 信息时代的来临使得企业营销焦点从产品中心转向客户中心&#xff0c;客户关系管理成为企业的核心问题&#xff08;点击文末“阅读原文”获取完整代码数据&#xff09;。 客户关系管理的关键是客…...

WebRTC的三大线程

WebRTC中的三个主要线程&#xff1a; signaling_thread&#xff0c;信号线程&#xff1a;用于与应用层交互worker_thread&#xff0c;工作线程&#xff08;最核心&#xff09;&#xff1a;负责内部逻辑处理network_thread&#xff0c;网络线程&#xff1a;负责网络数据包的收发…...

Spring SpEL表达式由浅入深

标题 前言概述功能使用字面值对象属性和方法变量引用#this 和 #root变量获取类的类型调用对象(类)的方法调用类构造器类型转换运算符赋值运算符条件(关系)表达式三元表达式Elvis 操作符逻辑运算instanceof 和 正则表达式的匹配操作符 安全导航操作员数组集合(Array 、List、Map…...

数据设计规范

目录 一、数据库设计的原则 二、表设计原则 三、其他设计规范 四、最佳实践 数据库设计&#xff08;Database Design&#xff09;是指对于一个给定的应用环境&#xff0c;构造最优的数据库模式&#xff0c;建立数据库及其应用系统&#xff0c;使之能够有效地存储数据&#…...

基于SpringBoot的宠物寄养系统的设计与实现(源码+SQL+LW+部署讲解)

文章目录 摘 要1. 第1章 选题背景及研究意义1.1 选题背景1.2 研究意义1.3 论文结构安排 2. 第2章 相关开发技术2.1 前端技术2.2 后端技术2.3 数据库技术 3. 第3章 可行性及需求分析3.1 可行性分析3.2 系统需求分析 4. 第4章 系统概要设计4.1 系统功能模块设计4.2 数据库设计 5.…...

深度学习中的HTTP:从请求到响应的计算机网络交互

在现代深度学习应用中&#xff0c;HTTP&#xff08;超文本传输协议&#xff09;不仅仅是网页浏览的基础协议&#xff0c;它也在机器学习和人工智能的服务架构中扮演着至关重要的角色。本文将深入探讨HTTP在深度学习中的应用&#xff0c;并阐明它如何支持模型的训练、推理及API服…...

Agent系列:AppAgent v2-屏幕智能Agent(详解版)

引言 简介 方法 Agent 框架 Agent 交互 探索阶段 部署阶段 文档生成 高级功能 实验结果 总结 局限性 未来工作 1. 引言 大语言模型&#xff08;LLM&#xff09;如 ChatGPT 和 GPT-4 显著提升了自然语言处理能力&#xff0c;并且推动了智能体在自主决策中的应用。…...

艾体宝方案丨全面提升API安全:AccuKnox 接口漏洞预防与修复

一、API 安全&#xff1a;现代企业的必修课 在现代技术生态中&#xff0c;应用程序编程接口&#xff08;API&#xff09;扮演着不可或缺的角色。从数据共享到跨平台集成&#xff0c;API 成为连接企业系统与外部服务的桥梁。然而&#xff0c;伴随云计算的普及与微服务架构的流行…...

开源的Vue低代码表单设计器 form-create-designer v3.2.9 版本发布,新增10多种功能

form-create-designer 是一款开源的低代码表单设计器&#xff0c;通过数据驱动表单渲染。可以通过拖拽的方式快速创建表单&#xff0c;提高开发者对表单的开发效率&#xff0c;节省开发者的时间。并广泛应用于在政务系统、OA系统、ERP系统、电商系统、流程管理等领域。 项目采…...

Android Killer 无法调用jd-gui.exe

在Android Killer 时&#xff0c;点击图标转换为java代码时&#xff0c;调用jd-gui.exe 弹窗无法验证发布者&#xff0c;点击运行没反应 第一种方案&#xff1a;开启组策略&#xff08;网上找的&#xff0c;没有效果&#xff09; 键盘WinR &#xff0c;在弹出窗口中输入gped…...

24.01.01 MyBatis

对sql拆分简化之后,更适合使用MyBatisPlus 搭建项目 引入依赖 <!--Mysql 驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!--Druid数据库连接池--><dependenc…...

游戏引擎学习第70天

这一节没讲什么主要是关于接下来要干的任务 开发过程概览 我们正在进行最后的总结&#xff0c;并计划接下来的步骤。目前的目标是创建一个包含所有必要组件的游戏引擎原型版本&#xff0c;目的是让这些部分能够协同工作并展现预期效果。通过这一过程&#xff0c;可以实验和探…...

深入理解 Spring Cloud 中的 Eureka、Ribbon 和 Feign

1.eureka自我保护机制是什么? Eureka的自我保护机制是一种针对网络异常情况的安全保护措施&#xff0c;旨在防止因为网络问题导致的服务注册中心&#xff08;Eureka Server&#xff09;与微服务实例之间的通信故障。当网络分区或其他形式的网络故障发生时&#xff0c;即使微服…...

DVWA靶场Brute Force (暴力破解) 漏洞low(低),medium(中等),high(高),impossible(不可能的)所有级别通关教程

目录 暴力破解low方法1方法2 mediumhighimpossible 暴力破解 暴力破解是一种尝试通过穷尽所有可能的选项来获取密码、密钥或其他安全凭证的攻击方法。它是一种简单但通常无效率的破解技术&#xff0c;适用于密码强度较弱的环境或当攻击者没有其他信息可供利用时。暴力破解的基…...

山高路陡,无人机代替滑轨吊运物资极大提高做作业效率降低成本

在山高路陡的地区&#xff0c;无人机代替传统的滑轨吊运物资&#xff0c;极大地提高了作业效率并降低了成本。以下是对这一现象的详细分析&#xff1a; 一、无人机吊运的优势 1. 提高作业效率&#xff1a; 无人机能够快速响应并执行吊运任务&#xff0c;尤其在高山、陡峭或交…...

数据的高级处理——pandas模块进阶——数据的统计运算

今天的学习用有好几处与书上的内容有出入&#xff0c;不只是因为pycharm中函数更新、弃用的问题&#xff0c;还是作者有些疏忽。不过影响不大&#xff0c;运行报错&#xff0c;GPT分析一下&#xff0c;原因很简单。这里不进行详细书名&#xff0c;在下边的代码上已经进行详细的…...

【Leetcode】3280. 将日期转换为二进制表示

文章目录 题目思路代码复杂度分析时间复杂度空间复杂度 结果总结 题目 题目链接&#x1f517; 给你一个字符串 date&#xff0c;它的格式为 yyyy-mm-dd&#xff0c;表示一个公历日期。 date 可以重写为二进制表示&#xff0c;只需要将年、月、日分别转换为对应的二进制表示&a…...

Vue3 中自定义hook

什么是hook&#xff1f;—— 本质是一个函数&#xff0c;把setup函数中使用的Composition API进行了封装&#xff0c;类似于vue2.x中的mixin。 自定义hook的优势&#xff1a;复用代码, 让setup中的逻辑更清楚易懂。 场景需求&#xff1a;现在我需要获取当前鼠标所点击的地方的…...

嵌入式系统 第七讲 ARM-Linux内核

• 7.1 ARM-Linux内核简介 • 内核&#xff1a;是一个操作系统的核心。是基于硬件的第一层软件扩充&#xff0c; 提供操作系统的最基本的功能&#xff0c;是操作系统工作的基础&#xff0c;它负责管理系统的进程、内存、设备驱动程序、文件和网络系统&#xff0c; 决定着系统的…...

音视频入门基础:MPEG2-PS专题(2)——使用FFmpeg命令生成ps文件

一、错误的命令 通过FFmpeg命令可以将mp4文件转换为ps文件&#xff0c;PS文件中包含PS流数据。 由于PS流/PS文件对应的FFInputFormat结构为&#xff1a; const FFInputFormat ff_mpegps_demuxer {.p.name "mpeg",.p.long_name NULL_IF_CONFIG_SMALL…...