我试图在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
有特殊要求吗?我是否需要在节点显示或类似之后调用它?
还有其他建议吗?
答案 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