在不拦截触摸事件的情况下检测 ViewGroup 中的长按

如何解决在不拦截触摸事件的情况下检测 ViewGroup 中的长按

我希望在我的 ViewGroup 中有以下行为:

  • 根本不拦截事件
  • 检测到长按后立即拦截
  • 仅在没有孩子检测到长按时才拦截

我认为,如果一个子节点在其 OnLongClickListener 上返回 true,这将导致 requestDisallowInterceptTouchEvent 向上传递视图层次结构,但这并没有发生...

有什么想法可以正确实施吗?

我目前的解决方案

class InterceptTouchFrameLayout @JvmOverloads constructor(
    context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0,defStyleRes: Int = 0
) : FrameLayout(context,attrs,defStyleAttr,defStyleRes) {

    var onEventListener: ((event: Event,location: Point) -> Unit)? = null
    var longPressDown: DownEvent? = null
    var longPressEventHandled: Boolean = false
    var longPressRunnable = Runnable {
        longPressDown?.let { onEventListener?.invoke(Event.LongPress,it.getPoint()) }
        longPressEventHandled = true
        L.d { "Long Press Event" }
    }

    // -----------------
    // Touch Events
    // -----------------

    /*
    * This method JUST determines whether we want to intercept the motion.
    * If we return true,onTouchEvent will be called and we do the actual
    * event handling there.
    */
    override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
        val intercept: Boolean // true if we want to intercept the touch event

        if (longPressEventHandled) {
            // the long press event has already been send out so we intercept - no child view needs to handle this event anymore!
            intercept = true
        } else {

            when (ev.actionMasked) {
                // Always handle the case of the touch gesture being complete.
                MotionEvent.ACTION_CANCEL,MotionEvent.ACTION_UP -> {
                    cancelLongPressDelayed()
                    intercept = false
                }
                MotionEvent.ACTION_DOWN -> {
                    startLongPressDelayed(ev)
                    intercept = false
                }
                MotionEvent.ACTION_MOVE -> {
                    val inValidDistance = longPressDown?.inClickDistance(ev,touchSlop) ?: false
                    // if we are not in a valid long press distance we cancel the long press
                    if (!inValidDistance) {
                        cancelLongPressDelayed()
                    }
                    intercept = false
                }

                else -> {
                    // In general,we don't want to intercept touch events. They should be
                    // handled by the child view.
                    intercept = false
                }
            }
        }
        L.d { "Intercept: ${ev.actionMasked} | $intercept | $longPressEventHandled" }
        return intercept
    }

    /* Here we actually handle the touch event
     * This method will only be called if the touch event was intercepted in
     * onInterceptTouchEvent
     */
    override fun onTouchEvent(ev: MotionEvent): Boolean {
        // we only intercept the touch event if we have detected a long press - otherwise we always forward the touch events to the children
        // => this function should NEVER be called when longPressEventHandled == false!
        L.d { "ev = ${ev.actionMasked} | longPressEventHandled = $longPressEventHandled" }

        val handled: Boolean
        when (ev.actionMasked) {
            // Always handle the case of the touch gesture being complete.
            MotionEvent.ACTION_CANCEL,MotionEvent.ACTION_UP -> {
                cancelLongPressDelayed()
                handled = true
            }
            else -> {
                handled = false
            }
        }
        return handled
    }

    override fun requestDisallowInterceptTouchEvent(disallowIntercept: Boolean) {
        super.requestDisallowInterceptTouchEvent(disallowIntercept)
        L.d { "disallowIntercept = $disallowIntercept" }
        cancelLongPressDelayed()
    }

    // -----------------
    // functions
    // -----------------

    private fun startLongPressDelayed(ev: MotionEvent) {
        longPressEventHandled = false
        longPressDown = DownEvent(ev.x.toInt(),ev.y.toInt())
        handler.postDelayed(
            longPressRunnable,(longPressTimeout.toFloat() * 1.1f).toLong() // we add a little bit to the timeout so that childrens long press handlers fire before us
        )
    }

    private fun cancelLongPressDelayed() {
        handler.removeCallbacks(longPressRunnable)
        longPressEventHandled = false
        longPressDown = null
    }

    // -----------------
    // classes / constants
    // -----------------

    private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop
    private val longPressTimeout = ViewConfiguration.getLongPressTimeout().toLong()

    class DownEvent(val x: Int,val y: Int) {
        fun inClickDistance(ev: MotionEvent,touchSlop: Int): Boolean {
            val distX = abs(ev.x.toInt() - x)
            val distY = abs(ev.y.toInt() - y)
            return distX <= touchSlop && distY <= touchSlop
        }

        fun getPoint() = Point(x,y)
    }

    enum class Event {
        LongPress
    }
}

我的解决方案有问题

我的 LongClickListeners 中确实有一个带有 ViewGroup 的项目 - 这些项目确实在其侦听器中返回 true 但这无济于事。我不知道如何检测孩子是否也在处理长按 - 在这种情况下,我的视图组应该立即取消它的长按处理...

似乎我需要在 ALL LongClickListeners 中手动执行以下操作:

view.parent.requestDisallowInterceptTouchEvent()

但这意味着视图必须始终知道它在我的自定义视图组中,并且必须手动调用 requestDisallowInterceptTouchEvent - 但这似乎不是很干净。这个问题有更好的解决方案吗?我不能在每个 OnLongClickListener 中手动执行某些操作?我可以在 ViewGroup 中执行什么操作,例如 doesAnyChildViewUnderMotionEventHandleThisLongPress() 之类的函数?有一个 View.hasOnLongClickListeners() 函数,但仅在 API >= 30 中,这对我的用例来说没问题,但似乎没有针对旧 API 的解决方案...

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-
参考1 参考2 解决方案 # 点击安装源 协议选择 http:// 路径填写 mirrors.aliyun.com/centos/8.3.2011/BaseOS/x86_64/os URL类型 软件库URL 其他路径 # 版本 7 mirrors.aliyun.com/centos/7/os/x86
报错1 [root@slave1 data_mocker]# kafka-console-consumer.sh --bootstrap-server slave1:9092 --topic topic_db [2023-12-19 18:31:12,770] WARN [Consumer clie
错误1 # 重写数据 hive (edu)&gt; insert overwrite table dwd_trade_cart_add_inc &gt; select data.id, &gt; data.user_id, &gt; data.course_id, &gt; date_format(
错误1 hive (edu)&gt; insert into huanhuan values(1,&#39;haoge&#39;); Query ID = root_20240110071417_fe1517ad-3607-41f4-bdcf-d00b98ac443e Total jobs = 1
报错1:执行到如下就不执行了,没有显示Successfully registered new MBean. [root@slave1 bin]# /usr/local/software/flume-1.9.0/bin/flume-ng agent -n a1 -c /usr/local/softwa
虚拟及没有启动任何服务器查看jps会显示jps,如果没有显示任何东西 [root@slave2 ~]# jps 9647 Jps 解决方案 # 进入/tmp查看 [root@slave1 dfs]# cd /tmp [root@slave1 tmp]# ll 总用量 48 drwxr-xr-x. 2
报错1 hive&gt; show databases; OK Failed with exception java.io.IOException:java.lang.RuntimeException: Error in configuring object Time taken: 0.474 se
报错1 [root@localhost ~]# vim -bash: vim: 未找到命令 安装vim yum -y install vim* # 查看是否安装成功 [root@hadoop01 hadoop]# rpm -qa |grep vim vim-X11-7.4.629-8.el7_9.x
修改hadoop配置 vi /usr/local/software/hadoop-2.9.2/etc/hadoop/yarn-site.xml # 添加如下 &lt;configuration&gt; &lt;property&gt; &lt;name&gt;yarn.nodemanager.res