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

Android修行手册-溢出父布局的按钮实现点击

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总
游戏脚本-辅助自动化Android控件全解手册再战Android系列
Scratch编程案例软考全系列Unity3D学习专栏
蓝桥系列ChatGPT和AIGC

👉关于作者

专注于Android/Unity和各种游戏开发技巧,以及各种资源分享(网站、工具、素材、源码、游戏等)
有什么需要欢迎底部卡片私我,交流让学习不再孤单

在这里插入图片描述

👉实践过程

有两种方案

😜方案一

方案一是在整个Activity窗口捕捉点击事件。

@Override
public boolean onTouchEvent(MotionEvent event) {//首先定义一个数组用来接收按钮的坐标xy值int[] xy = new int[2];//获取按钮的top/left xy值//button变量我在onCreat()函数中已经获取了控件,具体按实际情况写button.getLocationOnScreen(xy);//再定义一个数组用来计算控件的bottom/right xy值int[] xy_end = new int[2];xy_end[0] = buttom.getWidth() + xy[0];xy_end[1] = buttom.getHeight() + xy[1];//现在我们已经得到了按钮的左上坐标和右下坐标//两个点可以确定一个矩形嘛  event里包含了点击的信息;//我们判断点击的坐标是否在按钮坐标内,实际就是判断点击的xy值是否在上述矩形中;if (event.getX() >= xy[0] && event.getX() <= xy_end[0]&& event.getY() >= xy[1] && event.getY() <= xy_end[1]) {//如果是,那么就执行里边的代码,在这里我们可以callOnClick()按钮//实际体验了一番,发现轻点一下和长按均可以激活按钮;//但是,我的按钮拥有animate()事件,所以连续点击会在动画未完成时再次点击按钮,//所以我做了个判断,让动画未完成时不再执行点击,机制如我//实际中,读者完全不用这两行代码//让我看看有哪些读者看都不看直接复制代码--手动滑稽//虽说站在巨人肩膀上,但是也要搞懂其原理才不会摔下来。if (isMoreShow == false && xy[0] >= button.getHeight())return false;//我们callOnClick了按钮,也就是模拟点击了按钮;button.callOnClick();return false;}return super.onTouchEvent(event);
}

不足之处也很明显,如果页面点击事件要素过多,写入的判断就很多了,毕竟你是整个 Activity 自己处理事件了。

😜推荐方案二:委托

小应用场景:有时候一个按钮效果很小,就很难触发点击事件,我们通常会增大下这个点击区间范围。
大应用场景:我实现了多个脑图的功能,里面因为方便画线穿插过某个UI,就用到了此类知识。
在这里插入图片描述
其他情况多种多样,相信看这篇文章的你也是因为有这个需求才查找的。

小应用场景的实现很简单:

  1. 直接增大 View 的宽高,然后给View设置内边距 padding ;或者直接嵌套一层给这个父设置点击,但这会增加布局嵌套进而消耗性能。
  2. 利用委托功能直接增大点击的区间范围。
    /*** 扩展点击区域的范围* @param view       需要扩展的元素,此元素必需要有父级元素* @param expendSize 需要扩展的尺寸,当然也可以分别设置增大范围*/public static void expendTouchArea(final View view, final int expendSize) {if (view != null) {final View parentView = (View) view.getParent();parentView.post(new Runnable() {@Overridepublic void run() {Rect rect = new Rect();view.getHitRect(rect); rect.left -= expendSize;rect.top -= expendSize;rect.right += expendSize;rect.bottom += expendSize;parentView.setTouchDelegate(new TouchDelegate(rect, view));}});}}

事实是,委托就是系统给我们提供的扩大控件点击区域判断范围的代理方式,我们看下View类的源码。

class View{/*** The delegate to handle touch events that are physically in this view* but should be handled by another view.*/private TouchDelegate mTouchDelegate = null;public boolean onTouchEvent(MotionEvent event) {//...if (mTouchDelegate != null) {if (mTouchDelegate.onTouchEvent(event)) {return true;}}}/*** Sets the TouchDelegate for this View.*/public void setTouchDelegate(TouchDelegate delegate) {mTouchDelegate = delegate;}
}

从源码中可以看到如果设置了TouchDelegate,touchEvent会优先交给TouchDelegate来处理。

package android.view;
import android.graphics.Rect;
/*** Helper class to handle situations where you want a view to have a larger touch area than its* actual view bounds. The view whose touch area is changed is called the delegate view. This* class should be used by an ancestor of the delegate. To use a TouchDelegate, first create an* instance that specifies the bounds that should be mapped to the delegate and the delegate* view itself.* The ancestor should then forward all of its touch events received in its* {@link android.view.View#onTouchEvent(MotionEvent)} to {@link #onTouchEvent(MotionEvent)}.*/
public class TouchDelegate {private View mDelegateView;private Rect mBounds;private boolean mDelegateTargeted;public TouchDelegate(Rect bounds, View delegateView) {mBounds = bounds;mDelegateView = delegateView;}public boolean onTouchEvent(MotionEvent event) {int x = (int) event.getX();int y = (int) event.getY();boolean sendToDelegate = false;boolean hit = true;boolean handled = false;switch (event.getActionMasked()) {case MotionEvent.ACTION_DOWN:mDelegateTargeted = mBounds.contains(x, y);sendToDelegate = mDelegateTargeted;break;//...}if (sendToDelegate) {final View delegateView = mDelegateView;if (hit) {// Offset event coordinates to be inside the target viewevent.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);} else {// Offset event coordinates to be outside the target view (in case it does// something like tracking pressed state)int slop = mSlop;event.setLocation(-(slop * 2), -(slop * 2));}handled = delegateView.dispatchTouchEvent(event);}return handled;}
}

从源码中 可以看到,创建TouchDelegate 需要传入一个Rect(left,top,right,bottom) 和delegateView, onTouchEvent触发时,会通过这个Rect来判断点击事件是否落在区域内,如果是 则转发给代理view来处理该事件。

😜复杂场景实现-重点

子 View 超出父布局显示,然后触发点击事件,同样利用的委托功能,但是因为要处理 Touch 需要自定义一下。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:clipChildren="false"android:clipToPadding="false"android:id="@+id/rootLay"android:orientation="vertical"tools:context=".MainActivity"tools:ignore="HardcodedText,InOrMmUsage"><cn.akitaka.test.TestOverClickandroid:id="@+id/testLay"android:layout_width="200mm"android:layout_height="200mm"android:background="@color/crane_swl_color_3"android:clipChildren="false"android:clickable="true"android:clipToPadding="false">
<!--特别留意,因为是自定义的RelativeLayout,Touch时间默认只有个down,需要设置可点击才能回调所有的事件--><Buttonandroid:id="@+id/idBtnTest"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginStart="300mm"android:text="Excel"android:textSize="26mm" /></cn.akitaka.test.TestOverClick>
</LinearLayout>
/*** @author akitaka 2023/11/22 960576866@qq.com* @describe TestOverClick*/
public class TestOverClick extends RelativeLayout {public TestOverClick(Context context) {super(context);}public TestOverClick(Context context, AttributeSet attrs) {super(context, attrs);}public TestOverClick(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);}public TestOverClick(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}private void initClickRect() {View rootParent = ((View) getParent());// 获取父视图rootParent.post(() -> {// 将当前代码放在消息队列中异步执行Rect rect = new Rect();// 创建一个矩形对象// 获取当前视图的点击区域  如果太早执行本函数,会获取rect失败,因为此时UI界面尚未开始绘制,无法获得正确的坐标getHitRect(rect);rect.left -= 0;rect.top -= 0;//布局中控件是距离左300像素 控件本身是200 他俩的中间间距为100 加上按钮的本身宽度rect.right += AutoSizeUtils.mm2px(getContext(), 100) + btn.getWidth();rect.bottom += 0;rootParent.setTouchDelegate(new TouchDelegate(rect, this));  // 设置根视图的触摸委托为当前视图});}private Button btn;//外部的按钮对象设置public void setBtn(Button btn) {this.btn = btn;initClickRect();}@Overridepublic boolean onTouchEvent(MotionEvent event) {Log.e("TAG", "事件类型: " + event.getAction());int x = (int) event.getX();int y = (int) event.getY();
//        if () {  TODO  重点注意
//            //这个if判断是你点击的x、y坐标是否在按钮的范围内,不在的话直接进行return不处理即可
//            //具体的区间判断范围,就需要自己的项目具体调整了。
//            return true;
//        }switch (event.getAction()) {case MotionEvent.ACTION_DOWN:Log.e("TAG", "按下事件: " + btn);btn.setBackgroundResource(R.color.purple_200);break;case MotionEvent.ACTION_UP:btn.performClick();Log.e("TAG", "抬起事件: " + btn);HandlerUtils.INSTANCE.postRunnable(() -> {btn.setBackgroundResource(R.color.purple_700);}, 30);//30毫秒延迟break;default:break;}return super.onTouchEvent(event);}
}
public class MainActivity extends FragmentActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TestOverClick testLay = findViewById(R.id.testLay);Button idBtnTest = findViewById(R.id.idBtnTest);idBtnTest.setOnClickListener(v -> Log.e("TAG", "点击了内容: "));testLay.setBtn(idBtnTest);}
}

上面的注释简直是保姆级的了。

  1. 自定义 TestOverClick 嵌套了个子 Button 控件,设置android:clickable="true"可点击,设置属性android:clipChildren="false"android:clipToPadding="false"实现超出区域可见。
  2. 自定义 TestOverClick 有个方法 initClickRect 是用来设置点击响应区域的,咱们向右侧进行了扩大,红色为默认响应区域,经过计算:布局中控件是距离左300像素 控件本身是200 他俩的中间间距为100 加上按钮的本身宽度。右侧增加了绿框范围的响应区域。
    在这里插入图片描述
  3. 接着我们在 onTouchEvent 函数中做两个处理:处理一是判断下点击的区间,通过计算允许在按钮范围内处理,否则的话直接消耗事件,这样就假装模拟出了只响应按钮了。处理二是在事件中抬起的时候回调下按钮的模拟点击事件,就会进入业务逻辑。注意我们真正点击的其实是父控件,只不过模拟点击了按钮。
  4. 默认模拟点击是没有点击效果的,所以我们在 onTouchEvent 中 down 和 up 的时候自己更改下按钮背景状态即可完美实现点击UI变化。
  5. activity 中直接使用即可,我们内部需要用到按钮,记得要传递进去按钮对象。

😜题外

一个Parent只能设置一个View的TouchDelegate,设置多个时只有最后设置的生效。
如果想恢复 View 的触摸范围:

/*** 还原View的触摸和点击响应范围,最小不小于View自身范围*/
public static void restoreViewTouchDelegate(final View view) {((View) view.getParent()).post(new Runnable() {@Overridepublic void run() {Rect bounds = new Rect();bounds.setEmpty();TouchDelegate touchDelegate = new TouchDelegate(bounds, view);if (View.class.isInstance(view.getParent())) {((View) view.getParent()).setTouchDelegate(touchDelegate);}}});
}

还没懂?下方卡片联系我,手把手教你。

👉其他

📢作者:小空和小芝中的小空
📢转载说明-务必注明来源:https://zhima.blog.csdn.net/
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

温馨提示点击下方卡片获取更多意想不到的资源。
空名先生

相关文章:

Android修行手册-溢出父布局的按钮实现点击

Unity3D特效百例案例项目实战源码Android-Unity实战问题汇总游戏脚本-辅助自动化Android控件全解手册再战Android系列Scratch编程案例软考全系列Unity3D学习专栏蓝桥系列ChatGPT和AIGC &#x1f449;关于作者 专注于Android/Unity和各种游戏开发技巧&#xff0c;以及各种资源分…...

Transformer——decoder

上一篇文章&#xff0c;我们介绍了encoder&#xff0c;这篇文章我们将要介绍decoder Transformer-encoder decoder结构&#xff1a; 如果看过上一篇文章的同学&#xff0c;肯定对decoder的结构不陌生&#xff0c;从上面框中可以明显的看出&#xff1a; 每个Decoder Block有两个…...

基于 STM32 的温度测量与控制系统设计

本文介绍了如何基于 STM32 微控制器设计一款温度测量与控制系统。首先&#xff0c;我们将简要介绍 STM32 微控制器的特点和能力。接下来&#xff0c;我们将详细讨论温度传感器的选择与接口。然后&#xff0c;我们将介绍如何使用 STM32 提供的开发工具和相关库来进行温度测量和控…...

python之pyqt专栏3-QT Designer

从前面两篇文章python之pyqt专栏1-环境搭建与python之pyqt专栏2-项目文件解析&#xff0c;我们对QT Designer有基础的认识。 QT Designer用来创建UI界面&#xff0c;保存的文件是"xxx.ui"文件&#xff0c;"xxx.ui"可以被pyuic转换为"xxx.py",而&…...

【鸿蒙应用ArkTS开发系列】- 云开发入门实战二 实现省市地区三级联动地址选择器组件(下)

文章目录 概述端云调用流程端侧集成AGC SDK端侧省市地区联动的地址选择器组件开发创建省市数据模型创建省市地区视图UI子组件创建页面UI视图Page文件 打包测试总结 概述 我们在前面的课程&#xff0c;对云开发的入门做了介绍&#xff0c;以及使用一个省市地区联动的地址选择器…...

HCIA题目解析(1)

1、【多选题】关于动态 MAC 地址表说法正确的是&#xff1f; A、通过报文中的源MAC地址学习获得的动态MAC表项会老化 B、通过查看指定动态MAC地址表项的个数&#xff0c;可以获取接口下通信的用户数 C、在设备重启后&#xff0c;之前的动态表项会丢失 D、在设备重启后&…...

运维高级-day02

一、编写系统服务启动脚本 RHEL6风格 1、Linux运行级别 Linux运行有七个级别 级别 描述 0 停机状态&#xff0c;系统默认运行级别不能设置为0&#xff0c;否则系统不能正常启动。使用init0命令&#xff0c;可关闭系统 1 单用户状态&#xff0c;此状态仅root用户可登录。用…...

虹科分享 | 平衡速度和优先级:为多样化的实时需求打造嵌入式网络(2)——实时通信系统的需求

现代实时应用的复杂性和需求不断增加&#xff0c;需要强大而可靠的通信系统。正如本系列第一部分所述&#xff0c;这些应用涵盖从秒到毫秒的广泛响应时间要求&#xff0c;它们的成功通常取决于其响应的精确时间。因此&#xff0c;所选的通信系统必须能够满足这些严格的时序限制…...

佳易王各行业收银管理系统软件,企业ERP管理软件,企业或个体定制开发软件以及软件教程资源下载总目录,持续更新,可关注收藏查阅

系统简介 1、佳易王软件功能实用、操作简单、软件绿色免安装&#xff0c;解压即可使用&#xff0c;软件已经内置数据库&#xff0c;不需再安装其他数据库文件。 2、佳易王软件&#xff0c;已经形成系列&#xff0c;上百款管理系统软件涵盖多个行业。 3、已为多个企业个体定制…...

C_4练习题

一、单项选择题&#xff08;本大题共20小题&#xff0c;每小题2分&#xff0c;共40分。在每小题给出的四个备选项中选出一个正确的答案,并将所选项前的字母填写在答题纸的相应位置上。) 定义如下变量和数组&#xff1a; int i; int x[3][3]{1,2,3,4,5,6,7,8,9}; 则下面语句的输…...

自动化测试-Selenium

一. Selenium介绍 selenium 是用来做web自动化测试的框架,支持各种浏览器,各种,支持各种语言 原理: 二. 元素定位 2.1 XPath 定位 绝对路径: /html/head/title 相对路径以双斜杠开头,常见的相对路径定位有以下几种: <1>相对路径索引: 索引是从1开始的 <2>相…...

基于单片机的温湿度检测系统设计

目录 摘 要... 2 第一章 绪论... 5 1.1 研究课题背景... 5 1.2 国内外发展概况... 7 1.3 课题研究的目的... 8 1.4 课题的研究内容及章节安排... 8 第二章 温湿度检测系统控制系统的设计方案... 10 2.1 设计任务及要求... 10 2.2 温湿度检测系统总体设计方…...

C# 关于异常处理 try-catch语句的使用

在实际应用中&#xff0c;比如涉及文件读写、网络通信时&#xff0c;会因为文件不存在、权限不够、网络异常等原因引发异常&#xff0c;或者对数据库连接、查询、更新等操作&#xff0c;会因为连接超时、语法错误、唯一约束冲突等引发异常。 看过去的代码&#xff0c;当进行上…...

【LeeCode】26.删除有序数组中的重复项

给你一个 非严格递增排列 的数组 nums &#xff0c;请你原地删除重复出现的元素&#xff0c;使每个元素 只出现一次 &#xff0c;返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。 考虑 nums 的唯一元素的数量为 k &#xff0c;你需…...

4-Docker命令之docker create

1.docker create介绍 docker create命令是用于创建一个新的容器,等价于docker run -d命令,但是与docker run -d不同的是,docker create创建的容器并未实际启动,需要指定docker start命令启动。 2.docker create用法 docker create [参数] [root@centos79 ~]# docker cr…...

leetcode每日一题33

86.分隔链表 因为对链表中的一个节点进行更换位置的操作需要知道该节点的上一个节点 所以建立一个虚拟头节点 ListNode* pnew ListNode(-201,head);根据题意&#xff0c;我们需要找到第一个大于x或等于x的节点large 并且将第一个大于或等于x的节点large后的所有小于x的节点都…...

性能测试【一】:Jmeter的常用操作

性能测试【一】&#xff1a;Jmeter的常用操作 一、使用命令行方式运行Jmeter1、为什么2、怎么用3、示例4、结果文件 二、生成动态报告1、准备2、命令3、报告示例4、报告释义 三、使用问题汇总 推荐使用命令行运行&#xff0c;GUI方式会经常卡死&#xff0c;尤其跑稳定性 一、使…...

【JAVA】SpringBoot + mongodb 分页、排序、动态多条件查询及事务处理

【JAVA】SpringBoot mongodb 分页、排序、动态多条件查询及事务处理 1.引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- mongodb ↓ -->&…...

nrm安装及使用

一、介绍 nrm 是一个 Node.js 的 registry 管理工具&#xff0c;它允许你快速地在不同的 npm registry 之间进行切换。通过使用 nrm&#xff0c;你可以方便地将 npm 的 registry 切换为淘宝镜像、npm 官方镜像或者其他定制的镜像&#xff0c;以加快包的下载速度。nrm仓库请点击…...

docker报错standard init linux.go:228 exec user process caused: exec format error

1、报错 使用Dockerfile自己做的服务镜像&#xff0c;docker run时启动失败&#xff0c;报错如下&#xff1a; standard init linux.go:228 exec user process caused: exec format error2、原因一 当前服务器的CPU架构和构建镜像时的CPU架构不兼容。比如做镜像是在arm机器下…...

《从零掌握MIPI CSI-2: 协议精解与FPGA摄像头开发实战》-- CSI-2 协议详细解析 (一)

CSI-2 协议详细解析 (一&#xff09; 1. CSI-2层定义&#xff08;CSI-2 Layer Definitions&#xff09; 分层结构 &#xff1a;CSI-2协议分为6层&#xff1a; 物理层&#xff08;PHY Layer&#xff09; &#xff1a; 定义电气特性、时钟机制和传输介质&#xff08;导线&#…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

掌握 HTTP 请求:理解 cURL GET 语法

cURL 是一个强大的命令行工具&#xff0c;用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中&#xff0c;cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...

基于鸿蒙(HarmonyOS5)的打车小程序

1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...

LangChain 中的文档加载器(Loader)与文本切分器(Splitter)详解《二》

&#x1f9e0; LangChain 中 TextSplitter 的使用详解&#xff1a;从基础到进阶&#xff08;附代码&#xff09; 一、前言 在处理大规模文本数据时&#xff0c;特别是在构建知识库或进行大模型训练与推理时&#xff0c;文本切分&#xff08;Text Splitting&#xff09; 是一个…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter

java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用&#xff08;Math::max&#xff09; 2 函数接口…...

前端工具库lodash与lodash-es区别详解

lodash 和 lodash-es 是同一工具库的两个不同版本&#xff0c;核心功能完全一致&#xff0c;主要区别在于模块化格式和优化方式&#xff0c;适合不同的开发环境。以下是详细对比&#xff1a; 1. 模块化格式 lodash 使用 CommonJS 模块格式&#xff08;require/module.exports&a…...

Python的__call__ 方法

在 Python 中&#xff0c;__call__ 是一个特殊的魔术方法&#xff08;magic method&#xff09;&#xff0c;它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时&#xff08;例如 obj()&#xff09;&#xff0c;Python 会自动调用该对象的 __call__ 方法…...

【汇编逆向系列】六、函数调用包含多个参数之多个整型-参数压栈顺序,rcx,rdx,r8,r9寄存器

从本章节开始&#xff0c;进入到函数有多个参数的情况&#xff0c;前面几个章节中介绍了整型和浮点型使用了不同的寄存器在进行函数传参&#xff0c;ECX是整型的第一个参数的寄存器&#xff0c;那么多个参数的情况下函数如何传参&#xff0c;下面展开介绍参数为整型时候的几种情…...

深入理解 C++ 左值右值、std::move 与函数重载中的参数传递

在 C 编程中&#xff0c;左值和右值的概念以及std::move的使用&#xff0c;常常让开发者感到困惑。特别是在函数重载场景下&#xff0c;如何合理利用这些特性来优化代码性能、确保语义正确&#xff0c;更是一个值得深入探讨的话题。 在开始之前&#xff0c;先提出几个问题&…...