Android 扩大View可点击区域范围

news2025/1/18 20:23:46

有时候会遇到这种需求:本身控件显示在很小的范围内,但是要求扩大可点击的区域。根据官方文档https://developer.android.com/develop/ui/views/touch-and-input/gestures/viewgroup?hl=zh-cn#delegate可以得知通过 TouchDelegate 类,让父视图能够将子视图的可触摸区域扩展到子视图的边界之外。当子节点必须很小但需要更大的触摸区域时,这非常有用。
给个例子:
布局文件activity_main.xml如下:设置的TextView对应的宽和高都只有10dp

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
  tools:context=".MainActivity">

  <TextView
    android:id="@+id/tv_test"
    android:layout_width="10dp"
    android:layout_height="10dp"
    android:background="#e8e8e8"
    android:gravity="center"
    android:layout_centerInParent="true"
    android:text="Hello World" />

</RelativeLayout>

对面的activity文件如下:
扩大textView对应的上下左右点击区域为500
● int paddingLeft = 500;
● int paddingRight = 500;
● int paddingTop = 500;
● int paddingBottom = 500;

package com.example.addview;

import androidx.appcompat.app.AppCompatActivity;

import android.graphics.Rect;
import android.os.Bundle;
import android.view.TouchDelegate;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView testTextView = (TextView) findViewById(R.id.tv_test);
        testTextView.setOnClickListener(new View.OnClickListener() {
            int cnt = 0;
            @Override
            public void onClick(View v) {
                cnt++;
                Toast.makeText(MainActivity.this, "you clicked me" + " " + cnt + " times", Toast.LENGTH_SHORT).show();
            }
        });

        final View parent = (View) testTextView.getParent();
        int paddingLeft = 500;
        int paddingRight = 500;
        int paddingTop = 500;
        int paddingBottom = 500;
        testTextView.post(new Runnable() {
            @Override
            public void run() {
                Rect bounds = new Rect();
                testTextView.getHitRect(bounds);
                bounds.left -= paddingLeft;
                bounds.top -= paddingTop;
                bounds.right += paddingRight;
                bounds.bottom += paddingBottom;
                TouchDelegate mTouchDelegate = new TouchDelegate(bounds,testTextView);
                //设置parent的TouchDelegate,parent执行TouchDelegate的onTouchEvent方法会去调用代理的TextView的dispatchTouchEvent方法
                parent.setTouchDelegate(mTouchDelegate);
            }
        });
    }
}

然后截图就可以看到点击不是该控件显示的空白区域,也可以响应到该view的click事件。

在这里插入图片描述

总结:
通过PaddingLeft来扩大Android控件可点击区域的步骤如下:
● 首先,在 xml 文件中添加你的控件,并设置它的 paddingLeft 属性值。这个值应该设置为你希望扩大的像素值。
● 使用 TouchDelegate 类来创建一个代理对象,然后将它与控件绑定。代理对象告诉 Android 系统点击事件的触发范围。
● 然后使用 View.post 方法将扩大控件的宽度和高度的代码放在消息队列中。

那么为什么通过TouchDelegate类来创建的代理对象,与控件绑定以后可以扩大控件的宽度和高度呢?
从源码中可以看到如果设置了TouchDelegate,touchEvent会优先交给TouchDelegate来处理。

public class TouchDelegate {

    /**
     * View that should receive forwarded touch events
     */
    private View mDelegateView;

    /**
     * Bounds in local coordinates of the containing view that should be mapped to the delegate
     * view. This rect is used for initial hit testing.
     */
    // 传入一个Rect对象
    private Rect mBounds;

    /**
     * mBounds inflated to include some slop. This rect is to track whether the motion events
     * should be considered to be within the delegate view.
     */
    private Rect mSlopBounds;

    /**
     * True if the delegate had been targeted on a down event (intersected mBounds).
     */
    @UnsupportedAppUsage
    private boolean mDelegateTargeted;

    /**
     * The touchable region of the View extends above its actual extent.
     */
    public static final int ABOVE = 1;

    /**
     * The touchable region of the View extends below its actual extent.
     */
    public static final int BELOW = 2;

    /**
     * The touchable region of the View extends to the left of its actual extent.
     */
    public static final int TO_LEFT = 4;

    /**
     * The touchable region of the View extends to the right of its actual extent.
     */
    public static final int TO_RIGHT = 8;

    private int mSlop;

    /**
     * Touch delegate information for accessibility
     */
    private TouchDelegateInfo mTouchDelegateInfo;

    /**
     * Constructor
     *
     * @param bounds Bounds in local coordinates of the containing view that should be mapped to
     *        the delegate view
     * @param delegateView The view that should receive motion events
     */
    public TouchDelegate(Rect bounds, View delegateView) {
        // 接收对应的view控件和扩大的区域
        mBounds = bounds;

        mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
        mSlopBounds = new Rect(bounds);
        mSlopBounds.inset(-mSlop, -mSlop);
        mDelegateView = delegateView;
    }

    /**
     * Forward touch events to the delegate view if the event is within the bounds
     * specified in the constructor.
     *
     * @param event The touch event to forward
     * @return True if the event was consumed by the delegate, false otherwise.
     */
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        // 获取事件的坐标x,y
        int x = (int)event.getX();
        int y = (int)event.getY();
        boolean sendToDelegate = false;
        boolean hit = true;
        boolean handled = false;

        switch (event.getActionMasked()) {
            // 如果是ACTION_DOWN事件,判断事件的位置x,y是否落在扩大的区域mBounds内
            case MotionEvent.ACTION_DOWN:
                mDelegateTargeted = mBounds.contains(x, y);
                sendToDelegate = mDelegateTargeted;
                break;
            case MotionEvent.ACTION_POINTER_DOWN:
            case MotionEvent.ACTION_POINTER_UP:
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_MOVE:
                sendToDelegate = mDelegateTargeted;
                if (sendToDelegate) {
                    Rect slopBounds = mSlopBounds;
                    if (!slopBounds.contains(x, y)) {
                        hit = false;
                    }
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                sendToDelegate = mDelegateTargeted;
                mDelegateTargeted = false;
                break;
        }
        // 如果落在扩大的区域内
        if (sendToDelegate) {
            if (hit) {
                // 设置该事件对应的触发位置
                // Offset event coordinates to be inside the target view
                event.setLocation(mDelegateView.getWidth() / 2, mDelegateView.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 = mDelegateView.dispatchTouchEvent(event);
        }
        return handled;
    }
    ...
}

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

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

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

相关文章

Qt 各种数据类型

目录 1. 基础类型 2. log 输出 3. 字符串类型 3.2 QByteArray 构造函数 数据操作 子字符串查找和判断 遍历 查看字节数 类型转换 3.3 QString 4. QVariant 4.1 标准类型 4.2 自定义类型 5. 位置和尺寸 5.1 QPoint 5.2 QLine 5.3 QSize 5.4 QRect 6. 日期和…

Halcon WPF 开发学习笔记(0):开篇介绍

文章目录 文章专栏Halcon是什么&#xff1f;安装教学视频链接简单来说 Halcon快速开发环境确认新建项目 文章专栏 Halcon开发 Halcon是什么&#xff1f; 史上最全VisionPro和Halcon 的详细对比 Halcon简述 Halcon基础大全&#xff08;基础算子、高阶算子、数组、分割、字符检测…

AI视频智能分析系统在线监测垃圾满溢/堆放/暴露解决方案

一、背景需求 随着我国城市化进程的加快和居民生活水平的提高&#xff0c;垃圾围城的现象越来越严重。垃圾桶溢满、垃圾长时间暴露等现象&#xff0c;不仅严重污染了生态环境&#xff0c;同时也极大影响了市容市貌&#xff0c;并且对居民的身体健康也构成了威胁&#xff0c;因…

Figma软件的缺点和替代软件推荐

说到Figma软件&#xff0c;相信没有人不知道设计行业的人&#xff0c;尤去年Adobe以200亿美元收购Figma软件的消息&#xff0c;对设计行业影响很大。可想而知&#xff0c;Figma软件在设计行业人士眼中的地位是毋庸置疑的。的确&#xff0c;Figma软件的功能非常强大&#xff0c;…

JavaScript_document对象_方法_创建元素

1、document.createElement() document.createElement方法用来生成元素节点&#xff0c;并返回该节点 2、document.createTextNode() document.createTextNode方法用来生成文本节点&#xff08;Text实例&#xff09;&#xff0c;并返回该节点。它的参数是文本节点的内容 3、…

Win系统强制删除文件/文件夹

Win系统强制删除文件/文件夹 前言系统磁盘清理360强制删除NPM删除 前言 Win系统的用户删除文件/文件夹时&#xff0c;可能由于权限问题导致文件无法正常删除&#xff0c;下文介绍解决方案。当常规的删除不起作用时&#xff0c;可使用如下方案进行删除&#xff0c;包含系统磁盘…

V-REP和Python的联合仿真

机器人仿真软件 各类免费的的机器人仿真软件优缺点汇总_robot 仿真 软件收费么_dyannacon的博客-CSDN博客 课程地址 https://class.guyuehome.com/p/t_pc/course_pc_detail/column/p_605af87be4b007b4183a42e7 课程资料 guyueclass: 古月学院课程代码 旋转变换 旋转的左乘与…

灵魂拷问:读取 excel 测试数据真的慢吗?

&#x1f4e2;专注于分享软件测试干货内容&#xff0c;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01;&#x1f4e2;交流讨论&#xff1a;欢迎加入我们一起学习&#xff01;&#x1f4e2;资源分享&#xff1a;耗时200小时精选的「软件测试」资…

64位Office API声明语句第112讲

跟我学VBA&#xff0c;我这里专注VBA, 授人以渔。我98年开始&#xff0c;从源码接触VBA已经20余年了&#xff0c;随着年龄的增长&#xff0c;越来越觉得有必要把这项技能传递给需要这项技术的职场人员。希望职场和数据打交道的朋友&#xff0c;都来学习VBA,利用VBA,起码可以提高…

Linux开发工具的使用(vim、gcc/g++ 、make/makefile)

文章目录 一 &#xff1a;vim1:vim基本概念2:vim的常用三种模式3:vim三种模式的相互转换4:vim命令模式下的命令集- 移动光标-删除文字-剪切/删除-复制-替换-撤销和恢复-跳转至指定行 5:vim底行模式下的命令集 二:gcc/g1:gcc/g的作用2:gcc/g的语法3:预处理4:编译5:汇编6:链接7:函…

【ARFoundation学习笔记】ARFoundation基础(下)

写在前面的话 本系列笔记旨在记录作者在学习Unity中的AR开发过程中需要记录的问题和知识点。难免出现纰漏&#xff0c;更多详细内容请阅读原文。 文章目录 TrackablesTrackableManager可跟踪对象事件管理可跟踪对象 Session管理 Trackables 在AR Foundation中&#xff0c;平面…

大厂面试题-b树和b+树的理解

为了更清晰的解答这个问题&#xff0c;从三个方面来回答&#xff1a; a.了解二叉树、AVL树、B树的概念 b.B树和B树的应用场景 1.B树是一种多路平衡查找树&#xff0c;为了更形象的理解&#xff0c;我们来看这张图。 二叉树&#xff0c;每个节点支持两个分支的树结构&#xff…

第十五届全国交通运输领域青年学术会议,和鲸 Heywhale 携手龙船科技联合发布科研服务解决方案

2023年10月29日&#xff0c;由中国交通运输协会青年科技工作者工作委员会主办&#xff0c;集美大学承办的“第十五届全国交通运输领域青年学术会议”在一片热烈的氛围中圆满落幕。 本届会议以“低碳•智能•安全•可持续综合交通发展创新”为主题&#xff0c;围绕综合立体交通…

JavaScript_document对象_方法_获取元素

1、document.getElementsByTagName 2、 document.getElementsByClassName() document.getElementsByClassName方法返回一个类似数组的对象&#xff08;HTMLCollection实例&#xff09;&#xff0c;包括了所有class名字符合指定条件的元素&#xff0c;元素的变化实时反映在返回…

FL Studio21.2中文高级版数字音乐工作站(DAW)

FL Studio是一款功能强大的数字音乐工作站&#xff08;DAW&#xff09;&#xff0c;软件提供了丰富的功能和工具&#xff0c;使音乐制作变得更加轻松和富有创意性。而在其中&#xff0c;一个关键的功能就是Fruity Wrapper&#xff0c;它在FL Studio中扮演着重要的角色。接下来给…

idea必装插件EditStarters(快速引入依赖)

前言 一般来说我们要向一个 servlet 或者 Spring 项目中引入依赖都需要先到中心仓库找到对应的依赖&#xff0c;选择依赖的版本&#xff0c;把依赖添加到配置文件 pom.xml 中&#xff0c;这其实还是有点麻烦的&#xff0c;而通过 EditStarters 插件我们可以迅速的添加依赖到项目…

计算机网络第4章-IPv6和寻址

IP地址的分配 为了获取一块IP地址用于一个组织的子网内&#xff0c;于是我们向ISP联系&#xff0c;ISP则会从已分给我们的更大 地址块中提供一些地址。 例如&#xff0c;ISP也许已经分配了地址块200.23.16.0/20。 该ISP可以依次将该地址块分成8个长度相等的连续地址块&…

Jakarta-JVM篇

文章目录 一.前言1. 1 JVM-堆常用调参1.2 JVM-方法区常用参数1.3 JVM-codeCache 二.JVM内存结构三. 对象创建四. JVM垃圾回收算法4.1 可达性分析算法4.1.1 对象引用4.1.2 回收方法区. 4.2 分代回收4.3 标记清除4.4 标记复制4.5 标记整理 五.垃圾回收器5.1 根节点枚举5.2 安全点…

2023-11-06今日最大收获:坑爹的 JpaRepository!

1.坑爹的 JpaRepository&#xff01; org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet 2023-11-06 18:38:53.12…

光链路测试6271B光纤温度分布测试仪

6271B 光纤温度分布测试 6271B 光纤温度分布测试仪主要基于光纤拉曼散射效应和光时域反射技术研制&#xff0c;利用温度传感光缆&#xff08;纤&#xff09;&#xff0c;实现空间温度的在线、实时、连续分布式测试。光纤温度分布测试仪由主机&#xff08;信号处理单元&#xff…