4

我正在尝试为 JavaFX 2.2 中的不可见节点接收 MouseEvents。将其视为一个交互式但不可见的区域,它应该触发一个动作,例如当鼠标悬停它时。问题是,这不是一个静态定义的区域,但有多个区域(很多)可以由应用程序移动和调整大小。因此,对于我的用例来说,全局监听鼠标移动并执行手动检测 - 比如 - MouseMove-Events 将是很多开销。

目前,我正在试验一个透明的矩形new Rectangle(200, 100, Color.TRANSPARENT)(也必须在这些透明区域上检测到 MouseMove)。

我还希望得到帮助我更好地理解 JavaFX 2.2 通常如何根据节点的可见性处理 MouseEvents 的答案。

到目前为止,我的实验显示了以下一般见解:

  • 给定一个透明的场景:当用户点击一个透明区域时,鼠标事件只会被传递给外部应用程序(在场景下方)。当用户单击场景的可见像素时,无法将鼠标事件“传递给操作系统”。对?

  • 默认情况下,其他节点顶部的窗格将吞下任何 MouseEvent,除非它是 MouseTransparent 或 MouseClick 出现在不可见(透明)区域。

  • pickOnBounds(true|false)是否可以启用(true)基于边界(矩形)的 MouseEvents 检测或禁用它(false)。后者仅有效处理可见像素/区域的鼠标事件。pickOnBounds(true)似乎不适用于完全不可见的节点。对?

  • 我的实验表明,一个节点至少需要填充 -new Color(1,1,1,0.004)才能被视为可见。较低的 alpha 值被认为是不可见的,这会导致 MouseEvents 不被处理,即使pickOnBounds(true)已被调用。

我做对了吗?那么就没有办法让一个不可见的节点接收 MouseEvents。

还是对工作有特殊要求pickOnBounds?我是否需要仅在显示节点或类似内容后调用它?还有其他建议吗?

4

1 回答 1

2

简而言之:使用 Node.setOpacity(0.0)

opacity 属性控制节点的“视觉透明度”,而不影响它接收事件的能力,请参阅APIdocs。将此属性设置为零可以实现您(和我)正在寻找的效果:一个不可见但鼠标敏感的“热区”-Node.js。

这与Node.setVisible(false)我首先尝试的相反。该方法还禁用事件处理。来自 Node.setVisible() APIdocs:

不可见节点永远不会接收鼠标事件或键盘焦点,并且在它们变得不可见时永远不会保持键盘焦点。

“不可见”实际上意味着“调用后setVisible(false)”,不应与图像中的不透明度或完全透明像素相混淆。

由于缺乏声誉,我无法直接发布屏幕截图,因此: 链接到显示下面示例代码的热区布局的屏幕截图(由于显而易见的原因,屏幕截图中节点的不透明度未设置为 0)。

该示例使用一个 Group 作为热区,其中包含一个矩形和一个圆形来定义捕获鼠标事件的区域。只需要在 Group 上设置 opacity 属性和鼠标处理程序,而不是在子组上设置。

这样您就可以构建任意形状的热区。如果要将具有透明区域的图像用作热区,pickOnBounds则需要将其属性设置为,false以便考虑实际图像内容,而不仅仅是边界框。

希望能帮助到你!

public class HotZoneTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        StackPane root = new StackPane();
        Scene scene = new Scene(root, 300, 250);
        primaryStage.setScene(scene);
        primaryStage.show();

        Group hotZone = new Group();
        root.getChildren().add(hotZone);

        hotZone.getChildren().add(new Rectangle(10, 20, 100, 50));
        hotZone.getChildren().add(new Circle(50, 120, 20));

        hotZone.setOpacity(0.4); //set to 0.0 to make invisible

        EventHandler handler = new EventHandler() {
            @Override
            public void handle(Event e) {
                System.out.println("hotZone mouse event: " + e);
            }
        };
        hotZone.addEventHandler(MouseEvent.MOUSE_ENTERED, handler);
        hotZone.addEventHandler(MouseEvent.MOUSE_CLICKED, handler);
        hotZone.addEventHandler(MouseEvent.MOUSE_EXITED, handler);

    }

编辑:关于您的特定子问题(据我所知,我不是 FX 大师 :))

当用户单击场景的可见像素时,无法将鼠标事件“传递给操作系统”。对?

有意思,没试过。对可能起作用的纯粹推测:获取鼠标事件的屏幕坐标,将窗口移开,使用 java.awt.Robot 将操作系统光标移动到鼠标事件的坐标,如果需要,在那里单击,然后移动你的窗户回来。当心:听起来像一个彻头彻尾的黑客!

默认情况下,其他节点顶部的窗格将吞下任何 MouseEvent,除非它是 MouseTransparent 或 MouseClick 出现在不可见(透明)区域。

我也是这么理解的;虽然不确定鼠标进入/退出。对于那些您MOUSE_ENTERED_TARGET/MOUSE_EXITED_TARGET至少可以在父母中聆听以确定进入/退出哪个孩子的人。如果您想阻止子级接收事件,请在父级上注册一个事件过滤器并在那里使用该事件。

pickOnBounds(true|false) 用于启用(true)基于边界(矩形)的 MouseEvents 检测或禁用它(false)。后者仅有效处理可见像素/区域的鼠标事件。

是的。

pickOnBounds(true) 似乎不适用于完全不可见的节点。

对通过调用不可见的节点为真setInvisible(true)

我的实验表明,一个节点至少需要填充 - new Color(1,1,1,0.004) 才能被视为可见。

无法发表评论,但您的实验结果似乎不错。

那么就没有办法让一个不可见的节点接收 MouseEvents。

使用.setOpacity(0.0)使节点“视觉上不可见”,但仍接收事件并尊重 setbickOnBounds

于 2015-02-05T09:47:22.537 回答