PAYATHON 2026

QML 中如何让不规则形状分别响应按钮事件?

数据阿明

结论

可以将每个形状做成独立的交互组件,并让输入命中测试依据实际形状,而非默认的矩形边界。对于 Qt 6 的 QtQuick.Shapes,可设置 Shape.containsMode: Shape.FillContains,再用 TapHandler 和 HoverHandler 处理按压、点击和悬停。形状之间的空隙不会命中这些组件,通常无需再用一个覆盖整个窗口的对象分发事件。

Qt 6:每个形状使用 Shape 和输入处理器

下面是一个简化示例。ShapePath 描述多边形,Shape.FillContains 让命中测试依据填充区域进行。每个组件都可以有自己的路径和位置。

import QtQuick
import QtQuick.Shapes

Shape {
    id: piece
    width: 160
    height: 120

    // 按填充路径判断输入命中,而不是按外接矩形判断
    containsMode: Shape.FillContains

    property bool hovered: false
    property bool down: false
    signal clicked()

    ShapePath {
        fillColor: piece.down ? "#3978c5"
                              : piece.hovered ? "#6aa3e8" : "#4b8bd8"
        strokeColor: "#24527f"
        strokeWidth: 2

        startX: 10
        startY: 10
        PathLine { x: 145; y: 10 }
        PathLine { x: 120; y: 55 }
        PathLine { x: 150; y: 105 }
        PathLine { x: 20; y: 105 }
        PathLine { x: 10; y: 10 }
    }

    HoverHandler {
        onHoveredChanged: piece.hovered = hovered
    }

    TapHandler {
        onPressedChanged: piece.down = pressed
        onTapped: piece.clicked()
    }
}

可以多次实例化这个组件,并分别设置位置、尺寸和路径。TapHandler 的 pressed 状态可用于按下反馈,tapped 可用于处理点击完成事件。Qt Quick 输入处理器也支持触摸输入,具体手势行为应根据应用需求调整。

这个示例提供了自定义形状控件的交互骨架,并不包含完整的 Qt Quick Controls Button 功能。键盘焦点、无障碍语义、禁用状态和按钮样式等,需要根据产品需求补充。Shape.FillContains 会检查路径内部,路径越复杂,命中检查的开销越大;形状数量较多或路径复杂时,应留意性能。Qt 文档:Shape 的 containsMode

使用现成 Button 或其他自定义绘制方式

QQuickItem 默认根据矩形边界判断输入点是否位于其中。若要让自定义 QQuickItem 按不规则轮廓响应输入,可以在 C++ 子类中重写 contains(const QPointF &point) const。也可以使用 containmentMask,将命中测试交给实现了 contains() 的对象。事件分发期间会频繁调用此方法,因此判断逻辑应保持轻量。Qt 文档:QQuickItem::contains() 和 containmentMask

如果要保留 Qt Quick Controls Button 的现有行为,可以尝试为按钮设置合适的 containmentMask,并确认目标 Qt 版本、按钮内部结构和坐标映射均符合预期。另一种做法是用自定义形状组件处理绘制和交互,再按需补充键盘与无障碍支持。

为什么不建议用矩形 MouseArea 直接代表形状

MouseArea 默认响应矩形区域内的输入。将它直接放在不规则图形上方时,矩形覆盖的透明角落或形状间隙也可能命中。propagateComposedEvents 处理的是重叠 MouseArea 之间的组合事件传播,不会自动将矩形命中区域改成不规则轮廓。Qt 文档:MouseArea 的事件传播说明

如果用矩形 MouseArea 并在处理器中自行判断点是否位于多边形内,还需要处理悬停状态、按下后的移动和释放,以及如何将未处理事件传给底层控件。让形状本身参与命中测试,通常更容易逐个组件管理交互。

实现时需要留意

  • 形状与命中区域应一致。 如果图形带有透明边缘、描边或阴影,应明确这些区域是否可点击,并让命中逻辑符合预期。
  • 关注事件抓取。 按下后,指针移出形状再释放是否仍算点击,取决于交互设计和处理器行为。应检查按下、移出、移回和释放时的状态变化。
  • 处理重叠与层级。 即使形状在设计上互不重叠,也要留意父项和兄弟项的堆叠顺序,以及其他输入处理器对事件的影响。
  • 检查目标 Qt 版本。 Shape.containsMode 在 Qt Quick Shapes 1.11 引入。如果目标环境不支持,可以考虑通过 C++ 自定义 QQuickItem::contains() 实现精确命中,或使用适用于该版本的其他输入方案。

备注:内容仅供参考。