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/
📢这位道友请留步☁️,我观你气度不凡,谈吐间隐隐有王者霸气💚,日后定有一番大作为📝!!!旁边有点赞👍收藏🌟今日传你,点了吧,未来你成功☀️,我分文不取,若不成功⚡️,也好回来找我。

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

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

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

相关文章

【vue】v-model在表单元素上的应用

表单元素&#xff1a; https://blog.csdn.net/m0_67930426/article/details/134655644 使用模板 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title> </head><body>&l…

UDP实现群聊通信

服务器端 #include <myhead.h> #define UDPIP "192.168.115.92" #define UDPPORT 6666 //存储客户信息的链表结构体 typedef struct Node {char name[20];struct sockaddr_in cin;struct Node *next; }*linklist; //数据结构体 struct data_cli {char type;ch…

12.docker的网络-host模式

1.docker的host网络模式简介 host模式下&#xff0c;容器将不会虚拟出自己的网卡、配置IP等&#xff0c;而是使用宿主机的IP和端口&#xff1b;也就说&#xff0c;宿主机的就是我的。 2. 以host网络模式创建容器 2.1 创建容器 我们仍然以tomcat这个镜像来说明一下。我们以h…

Ubuntu 22.03 LTS 安装deepin-terminal 实现 终端 分屏

deepin-terminal 安装 源里面自带了这个软件&#xff0c;可以直接装 sudo apt install deepin-terminal 启动 按下Win键&#xff0c;输入deep即可快速检索出图标&#xff0c;点击启动 效果 分屏 CtrlShiftH 水平分割 CtrlShiftJ 垂直分割 最多分割成四个小窗口&#xff0…

C语言数据结构之顺序表(上)

前言&#xff1a; ⭐️此篇博文主要分享博主在学习C语言的数据结构之顺序表的知识点时写的笔记&#xff0c;若有错误&#xff0c;还请佬指出&#xff0c;一定感谢&#xff01;制作不易&#xff0c;若觉得内容不错可以点赞&#x1f44d;收藏❤️&#xff0c;这是对博主最大的认可…

基于C#实现并查集

一、场景 有时候我们会遇到这样的场景&#xff0c;比如:M{1,4,6,8},N{2,4,5,7}&#xff0c;我的需求就是判断{1,2}是否属于同一个集合&#xff0c;当然实现方法有很多&#xff0c;一般情况下&#xff0c;普通青年会做出 O(MN)的复杂度&#xff0c;那么有没有更轻量级的复杂度呢…

完美解决k8s master节点无法ping node节点中的IP或Service NodePort的IP

1、问题一 使用搭建好了K8S集群&#xff0c;先是node节点加入k8s集群时&#xff0c;用的内网IP&#xff0c;导致master节点无法操作node节点中的pod&#xff08;这里的不能操作&#xff0c;指定是无法查看node节点中pod的日志、启动描述、无法进入pod内部&#xff0c;即 kubec…

游戏开发引擎Cocos Creator和Unity如何对接广告-AdSet聚合广告平台

在游戏开发方面&#xff0c;游戏引擎的选择对开发过程和最终的产品质量有着重大的影响&#xff0c;Unity和Cocos是目前全球两大商用、通用交互内容开发工具&#xff0c;这两款引擎受到广泛关注&#xff0c;本文将从多个维度对两者进行比较&#xff0c;为开发者提供正确的选择建…

SEO工具-免费功能最全的5款SEO工具

随着互联网的蓬勃发展&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;已经成为许多企业和个人网站必备的关键技能。然而&#xff0c;对于初学者或者运营小型网站的人来说&#xff0c;使用专业的SEO工具可能涉及较高的成本。在这篇文章中&#xff0c;我们将向您推荐五款高…

U4_2:图论之MST/Prim/Kruskal

文章目录 一、最小生成树-MST生成MST策略一些定义 思路彩蛋 二、普里姆算法&#xff08;Prim算法&#xff09;思路算法流程数据存储分析 伪代码时间复杂度分析 三、克鲁斯卡尔算法&#xff08;Kruskal算法&#xff09;分析算法流程并查集-Find-set 伪代码时间复杂度分析 一、最…

OpenWrt Lan口上网设置

LAN口上网设置 连接上openwrt&#xff0c;我用的 倍控N5105&#xff0c;eth0&#xff0c;看到Openwrt的IP是10.0.0.1 在 网络 -> 网口配置 -> 设置好 WAN 口和 LAN 口 初次使用经常重置 openwrt 所以我设置的是 静态IP模式 - 网络 -> 防火墙 -> 常规设置 ->…

IDEA2023版本创建Sping项目只能勾选17和21,却无法使用Java8?(已解决)

方案&#xff1a;替换创建项目的源 我们只知道IDEA页面创建Spring项目&#xff0c;其实是访问spring initializr去创建项目。故我们可以通过阿里云国服去间接创建Spring项目。将https://start.spring.io/或者http://start.springboot.io/替换为 https://start.aliyun.com/

什么是量子优势?

量子优势是量子计算领域正在积极努力的里程碑&#xff0c;量子计算机可以解决最强大的非量子或经典计算机无法解决的问题。 量子是指原子和分子的尺度&#xff0c;在这个尺度上&#xff0c;我们所经历的物理定律被打破&#xff0c;并且应用了一组不同的、违反直觉的定律。量子…

Linux——vim编辑文件时——.swp文件解决方案

test.cpp样例 当我们vim test.cpp进入编辑文件。 却忘记了保存退出 再次进入就会出现一下画面 当你摁下Enter键位 出现以下几个选项 O——是只读不写 E——是正常打开文件但不会载入磁盘内容 R——覆盖——是加载存储磁盘的文件(当我们忘记保存时&#xff0c;系统会自动帮我…

Django二转Day02

http #1 http 是什么#2 http特点#3 请求协议详情 -请求首行---》请求方式&#xff0c;请求地址&#xff0c;请求协议版本 -请求头---》key:value形式 -referer&#xff1a;上一次访问的地址 -user-agenet&#xff1a;客户端类型 -name&#x…

百度人工智能培训第一天笔记

参加了百度人工智能初步培训&#xff0c;主要是了解一下现在人工智能的基本情况&#xff0c;以便后续看可以参与一些啥&#xff1f; 下面就有关培训做一些记录&#xff0c;以便后续可以继续学习。 一、理论基础部分 二、实际操作部分 主要学习的百度人工智能平台如下&#xf…

同旺科技 USB 转 RS-485 适配器 -- 隔离型(定制款)

内附链接 1、USB 转 RS-485 适配器 隔离版主要特性有&#xff1a; ● 支持USB 2.0/3.0接口&#xff0c;并兼容USB 1.1接口&#xff1b; ● 支持USB总线供电&#xff1b; ● 支持Windows系统驱动&#xff0c;包含WIN10 / WIN11 系统32 / 64位&#xff1b; ● 支持Windows …

不确定度校准和可靠性图简介

图片来源 项杰 一、说明 不确定性校准是机器学习中最容易被误解的概念之一。它可以概括为这个简单的问题&#xff1a;“鉴于上述下雨的可能性&#xff0c;您是否带伞&#xff1f;” 我们在日常生活中使用主观概率和不确定性校准的概念&#xff0c;但没有意识到它们。对于不确定…

西南科技大学(数据结构A)期末自测练习二

一、填空题(每空1分,共10分) 1、在线性表的下列运算中,不改变数据元素之间结构关系的运算是( D ) A、插入 B、删除 C、排序 D、定位 2、顺序表中第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是( B ) A.110 B.108 C.100 …

Debian10安装VMware Tools

一、原系统 首先我在界面按CTRLALTT和CTRLSiftT都没有反应&#xff0c;没关系&#xff0c;我有办法 系统版本 管理员用户 步骤一&#xff1a;打开VMware Tools文件 步骤二、将文件复制到自己熟悉的文件内 步骤三、命令行查看文件是否复制成功存在 步骤四、解压VMware-tools…