You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何在触发QTapAndHoldGesture后忽略QTapGesture?

Qt手势冲突解决:TapAndHold后屏蔽多余Tap事件

问题重现

我需要在自定义Widget中分别响应QTapAndHoldGesture与QTapGesture并执行不同操作,因此重写了QWidget::event方法,代码如下:

bool event(QEvent *event) override { 
    if (event->type() == QEvent::Gesture) { 
        auto g_event = static_cast<QGestureEvent *>(event); 
        qDebug() << "GestureEvent BEGIN: gestures " << g_event->gestures().size() << ", active: " << g_event->activeGestures(); 
        if (auto g = qobject_cast<QTapGesture *>(g_event->gesture(Qt::TapGesture))) { 
            g_event->accept(g); 
            return true; 
        } 
        if (auto g = qobject_cast<QTapAndHoldGesture *>(g_event->gesture(Qt::TapAndHoldGesture))) { 
            if (g->state() == Qt::GestureFinished) { 
                qDebug("FINISHED!!!"); 
                g->setGestureCancelPolicy(QGesture::CancelAllInContext); 
            } 
            g_event->accept(g); 
            return true; 
        } 
    }
    // 记得调用父类的event处理其他事件
    return QWidget::event(event);
}

但目前遇到问题:触发QTapAndHoldGesture并完成后,会收到不想要的QTapGesture事件,日志显示流程为QTapGesture启动→更新→QTapAndHoldGesture启动→完成→QTapGesture更新→完成。我希望忽略该多余的QTapGesture,但不想通过收集事件位置、时间来重新实现手势框架,因为无法关联这两个手势事件,请问是否有无需收集事件位置时间即可实现的方法?

解决方案

这个问题我之前做Qt手势开发时也碰到过,其实不用自己搞一套事件追踪,Qt本身就有原生方法解决。

问题的核心是Qt的手势识别逻辑:当你按住屏幕超过TapAndHold的阈值(默认700ms)后抬起,系统会先完成TapAndHold手势,但之前已经启动的Tap手势会继续走完流程,最终触发多余的Tap事件。我们可以利用Qt提供的手势取消接口,在TapAndHold完成时主动终止对应的Tap手势,就能避免后续的Tap事件触发。

核心修改代码

修改你的TapAndHold处理逻辑,加入cancelGesture调用:

if (auto g = qobject_cast<QTapAndHoldGesture *>(g_event->gesture(Qt::TapAndHoldGesture))) {
    if (g->state() == Qt::GestureFinished) {
        qDebug("FINISHED!!!");
        // 关键:取消当前上下文的Tap手势,阻止其后续触发
        g_event->cancelGesture(Qt::TapGesture);
        g->setGestureCancelPolicy(QGesture::CancelAllInContext);
    }
    g_event->accept(g);
    return true;
}

双重保险优化

为了避免极端情况下cancelGesture未及时生效,还可以在处理Tap手势时,额外检查是否有活跃或已完成的TapAndHold手势:

if (auto g = qobject_cast<QTapGesture *>(g_event->gesture(Qt::TapGesture))) {
    // 检查是否存在TapAndHold手势(活跃或已触发)
    bool hasTapAndHold = false;
    // 检查当前事件中的所有手势
    for (auto gest : g_event->gestures()) {
        if (qobject_cast<QTapAndHoldGesture*>(gest)) {
            hasTapAndHold = true;
            break;
        }
    }
    // 再检查活跃手势列表
    for (auto gest : g_event->activeGestures()) {
        if (qobject_cast<QTapAndHoldGesture*>(gest)) {
            hasTapAndHold = true;
            break;
        }
    }
    if (hasTapAndHold) {
        // 忽略这个Tap手势
        g_event->ignore(g);
        return false;
    }
    g_event->accept(g);
    return true;
}

原理说明

  • QGestureEvent::cancelGesture()会直接通知手势管理器终止指定类型的手势,中断该手势的后续状态变化,自然就不会再触发TapGesture的Finished事件了。
  • 双重检查的逻辑是兜底方案:万一cancelGesture没有及时生效,我们在处理Tap手势时也能判断出已经处理过TapAndHold,从而忽略掉多余的Tap事件。

这样既不用自己维护位置、时间等状态,完全基于Qt原生的手势框架就能解决冲突问题。

内容的提问来源于stack exchange,提问作者user1244932

火山引擎 最新活动