JavaFX 2.2 Mouseevent用于不可见节点

时间:2014-08-19 13:39:30

标签: javafx-2 mouseevent visibility invisible scenegraph

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

目前,我正在尝试使用透明矩形(new Rectangle(200, 100, Color.TRANSPARENT)),但实际/最终应用程序将使用某种类型的窗格,因为它实际上是一个可拖动的容器其他组件(当没有组件时,它有透明区域,并且必须在这些透明区域上检测MouseMoves。)

我还希望能够帮助我更好地理解JavaFX 2.2如何根据节点的可见性来处理MouseEvents。

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

  • 给定透明场景: 当用户单击透明区域时,鼠标事件将仅传递到外部应用程序(在场景下可视化)。没有办法将鼠标事件传递给操作系统"当用户点击场景的可见像素时。右

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

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

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

我做对了吗?然后,不可见的节点将无法接收MouseEvents。

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

1 个答案:

答案 0 :(得分:2)

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

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

这与我先尝试的Node.setVisible(false)形成鲜明对比。该方法还禁用事件处理。来自Node.setVisible()APIdocs:

  

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

"隐形"在致电setVisible(false)"之后,确实意味着"不应与图像中的不透明度或完全透明像素混淆。

由于缺乏声誉,我无法直接发布截图,因此: link to screenshot显示下面示例代码的热区布局(由于显而易见的原因,节点的不透明度在屏幕截图中未设置为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或鼠标单击出现在   不可见(透明)区域。

这也是我对它的理解;虽然不确定鼠标是否进入/退出。对于那些你可以至少在父母身上听MOUSE_ENTERED_TARGET/MOUSE_EXITED_TARGET来确定哪个孩子被输入/退出的人。如果要阻止子级接收事件,请在父级上注册事件过滤器并在此处使用事件。

  

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

  

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

如果通过调用setInvisible(true)使节点不可见,则为真。

  

我的实验表明,一个节点至少需要填充 - 新的   颜色(1,1,1,0.004)被认为是可见的。

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

  

然后,不可见的节点无法接收MouseEvents。

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