java 如何在 JavaFX 中以编程方式触发鼠标事件?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/40041625/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to fire mouse event programmatically in JavaFX?
提问by Dims
The following code show two panels, yellow and blue, where blue is a child of yellow.
以下代码显示了两个面板,黄色和蓝色,其中蓝色是黄色的子代。
If I click into the center of blue panel, mouse event is handled with both panels.
如果我点击蓝色面板的中心,鼠标事件由两个面板处理。
How to simulate the same behavior programmatically?
如何以编程方式模拟相同的行为?
In the Fire event
button I tried to do this, but failed: generated event appeared to handled only by yellow pane.
在Fire event
按钮中,我尝试执行此操作,但失败了:生成的事件似乎仅由黄色窗格处理。
Is it possible to post event so that it processed by all children as if it happen with "native" events?
是否可以发布事件,以便所有孩子都像处理“本地”事件一样处理它?
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.stage.Stage;
public class FireMouseEventProgrammatically extends Application {
@Override
public void start(Stage primaryStage) throws Exception {
BorderPane root = new BorderPane();
AnchorPane yellowPane = new AnchorPane();
yellowPane.setBackground(new Background(new BackgroundFill(Color.YELLOW, CornerRadii.EMPTY, Insets.EMPTY)));
yellowPane.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
System.out.print("yellowPane clicked ");
printMouseEvent(event);
System.out.println("");
}
});
root.setCenter(yellowPane);
Pane bluePane = new Pane();
bluePane.setBackground(new Background(new BackgroundFill(Color.BLUE, CornerRadii.EMPTY, Insets.EMPTY)));
bluePane.setOnMouseClicked(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
System.out.print("bluePane clicked ");
printMouseEvent(event);
System.out.println("");
}
});
AnchorPane.setLeftAnchor(bluePane, 200.);
AnchorPane.setRightAnchor(bluePane, 200.);
AnchorPane.setTopAnchor(bluePane, 200.);
AnchorPane.setBottomAnchor(bluePane, 200.);
yellowPane.getChildren().add(bluePane);
FlowPane buttonPane = new FlowPane();
buttonPane.setHgap(5);
buttonPane.setPadding(new Insets(5, 5, 5, 5));
root.setBottom(buttonPane);
Button fireEventButton = new Button();
fireEventButton.setText("Fire event");
fireEventButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
double blueX = bluePane.getWidth()/2;
double blueY = bluePane.getHeight()/2;
Point2D screenCoords = bluePane.localToScreen(blueX, blueY);
Point2D sceneCoords = bluePane.localToScene(blueX, blueY);
Event.fireEvent(yellowPane, new MouseEvent(MouseEvent.MOUSE_CLICKED,
sceneCoords.getX(), sceneCoords.getY(), screenCoords.getX(), screenCoords.getY(), MouseButton.PRIMARY, 1,
true, true, true, true, true, true, true, true, true, true, null));
}
});
buttonPane.getChildren().add(fireEventButton);
Scene scene = new Scene(root, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
private void printMouseEvent(MouseEvent event) {
System.out.print(String.format("x = %f, y = %f, xScreen = %f, yScreen = %f", event.getX(), event.getY(), event.getScreenX(), event.getScreenY()));
}
public static void main(String[] args) {
FireMouseEventProgrammatically.launch(args);
}
}
UPDATE
更新
I can't mention child node explicitly, because in general case it can be numerous number of children.
我不能明确提及子节点,因为在一般情况下,它可以是多个子节点。
How would I know which one to target?
我怎么知道要定位哪一个?
I would like to pass coordinates and let system determine targets automatically.
我想传递坐标并让系统自动确定目标。
回答by DVarga
The difference between the actual (phisycal) clicking and your programmatic clicking is, that in the first case the original event target is bluePane
(as it is the "uppermost" Node
visually), in the latter case the target is yellowPane
.
实际(物理)点击和程序化点击之间的区别在于,在第一种情况下,原始事件目标是bluePane
(因为它在Node
视觉上是“最上面的” ),在后一种情况下,目标是yellowPane
.
So, when the route construction occurs (please refer to the Event Delivery Process section) in the first case the route will be root
-> yellowPane
-> bluePane
, and in the second case it will be only root
-> yellowPane
, hence the bluePane
is untouched by the event.
因此,当路由构建发生时(请参阅事件传递过程部分),在第一种情况下,路由将是root
-> yellowPane
-> bluePane
,而在第二种情况下,它只会是 root
-> yellowPane
,因此bluePane
不会受到事件的影响。
Based on this you can target the bluePane
:
基于此,您可以定位bluePane
:
Event.fireEvent(bluePane, new MouseEvent(MouseEvent.MOUSE_CLICKED,
sceneCoords.getX(), sceneCoords.getY(), screenCoords.getX(), screenCoords.getY(), MouseButton.PRIMARY, 1,
true, true, true, true, true, true, true, true, true, true, null));
Or you can use a Robot
for example to generate the click:
或者您可以使用Robot
例如生成点击:
try {
robot = new java.awt.Robot();
robot.mouseMove((int)screenCoords.getX(), (int)screenCoords.getY());
robot.mousePress(16);
robot.mouseRelease(16);
robot.mouseMove((int) originalLocation.getX(), (int)originalLocation.getY());
} catch (AWTException e) {
e.printStackTrace();
}
Update 1:
更新 1:
If you want to stick with the JavaFX way, the question is now how to determine the "uppermost" Node
on a position.
如果您想坚持使用 JavaFX 方式,现在的问题是如何确定Node
位置上的“最上层”。
This is one thing that is not possible in a clean way. There is already a feature requestabout this.
这是不可能以干净的方式进行的一件事。已经有一个关于此的功能请求。
I have found this on helper method on fxexperiencethat can be used to determinte the Node
on a certain position:
我在fxexperience 的辅助方法上发现了这个,它可以用来确定Node
某个位置的:
public static Node pick(Node node, double sceneX, double sceneY) {
Point2D p = node.sceneToLocal(sceneX, sceneY, true /* rootScene */);
// check if the given node has the point inside it, or else we drop out
if (!node.contains(p)) return null;
// at this point we know that _at least_ the given node is a valid
// answer to the given point, so we will return that if we don't find
// a better child option
if (node instanceof Parent) {
// we iterate through all children in reverse order, and stop when we find a match.
// We do this as we know the elements at the end of the list have a higher
// z-order, and are therefore the better match, compared to children that
// might also intersect (but that would be underneath the element).
Node bestMatchingChild = null;
List<Node> children = ((Parent)node).getChildrenUnmodifiable();
for (int i = children.size() - 1; i >= 0; i--) {
Node child = children.get(i);
p = child.sceneToLocal(sceneX, sceneY, true /* rootScene */);
if (child.isVisible() && !child.isMouseTransparent() && child.contains(p)) {
bestMatchingChild = child;
break;
}
}
if (bestMatchingChild != null) {
return pick(bestMatchingChild, sceneX, sceneY);
}
}
return node;
}
that you can use like:
你可以像这样使用:
Node pick = pick(root, sceneCoords.getX(), sceneCoords.getY());
Event.fireEvent(pick, new MouseEvent(MouseEvent.MOUSE_CLICKED,
sceneCoords.getX(), sceneCoords.getY(), screenCoords.getX(), screenCoords.getY(), MouseButton.PRIMARY, 1,
true, true, true, true, true, true, true, true, true, true, null));
Update2:
更新2:
You can also use the deprecated methodNode.impl_pickNode(PickRay pickRay, PickResultChooser result)
to get the intersected Node
on a scene position:
您还可以使用已弃用的方法Node.impl_pickNode(PickRay pickRay, PickResultChooser result)
来获取Node
场景位置的相交:
PickRay pickRay = new PickRay((int) sceneCoords.getX(), (int) sceneCoords.getY(), 1.0, 1.0, 1.0);
PickResultChooser pickResultChooser = new PickResultChooser();
root.impl_pickNode(pickRay, pickResultChooser);
Node intersectedNode = pickResultChooser.getIntersectedNode();
You can use also this Node
as target similarly.
您也可以Node
类似地将此用作目标。
回答by Dominik Kunicki
The location of your MouseEvent is within both blue and yellow panes (blue one is inside yellow). If you do:
MouseEvent 的位置在蓝色和黄色窗格中(蓝色在黄色中)。如果你这样做:
Event.fireEvent(bluePane, new MouseEvent(MouseEvent.MOUSE_CLICKED,
sceneCoords.getX(), sceneCoords.getY(), screenCoords.getX(), screenCoords.getY(), MouseButton.PRIMARY, 1,
true, true, true, true, true, true, true, true, true, true, null));
It will work as You expected.
它会按您的预期工作。
JavaFX could not figure out what component was clicked base on location of a mouse event. You have to explicitly pass the target of the event. In that case it would be bluePane.
JavaFX 无法根据鼠标事件的位置确定单击了哪个组件。您必须明确传递事件的目标。在这种情况下,它将是 bluePane。
More information you can find here: https://docs.oracle.com/javafx/2/events/processing.htm
您可以在此处找到更多信息:https: //docs.oracle.com/javafx/2/events/processing.htm
回答by Hypnic Jerk
If I understood you correctly, you want to get the children of the yellowPane, and fire each of their events.
如果我理解正确,您想要获取 YellowPane 的子项,并触发它们的每个事件。
I accomplished this like so
我是这样完成的
Where ObservableList<Node> nodes
is the result of yellowPane.getChildren()
ObservableList<Node> nodes
结果在哪里yellowPane.getChildren()
private void clickOnMe(ObservableList<Node> nodes){
for(Node n : nodes){
n.fireEvent(new MouseEvent(MouseEvent.MOUSE_CLICKED,
n.getLayoutX(), n.getLayoutY(), n.getLayoutX(), n.getLayoutY(), MouseButton.PRIMARY, 1,
true, true, true, true, true, true, true, true, true, true, null));
}
}
This will grab every child of yellowPane
, which you said could be many, and fire each of their events. Hope this is what you wanted.
这将抓住 的每个孩子yellowPane
,你说可能有很多,并触发他们的每个事件。希望这是你想要的。