[英]How to fire mouse event programmatically in JavaFX?
以下代碼顯示了兩個面板,黃色和藍色,其中藍色是黃色的子代。
如果我點擊藍色面板的中心,鼠標事件由兩個面板處理。
如何以編程方式模擬相同的行為?
在Fire event
按鈕中,我嘗試執行此操作,但失敗了:生成的事件似乎僅由黃色窗格處理。
是否可以發布事件,以便所有孩子都像處理“本機”事件一樣處理它?
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);
}
}
更新
我不能明確提及子節點,因為在一般情況下,它可以是多個子節點。
我怎么知道要定位哪一個?
我想傳遞坐標並讓系統自動確定目標。
實際(物理)點擊和您的編程點擊之間的區別在於,在第一種情況下,原始事件目標是bluePane
(因為它在視覺上是“最上層” Node
),在后一種情況下,目標是yellowPane
。
因此,當路由構建發生時(請參閱事件傳遞過程部分),在第一種情況下,路由將是root
-> yellowPane
-> bluePane
,在第二種情況下它只會是root
-> yellowPane
,因此是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));
或者您可以使用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();
}
更新 1:
如果您想堅持使用 JavaFX 方式,現在的問題是如何確定位置上的“最上層” Node
。
這是不可能以干凈的方式進行的一件事。 已經有一個關於此的功能請求。
我在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;
}
你可以像這樣使用:
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));
更新2:
您還可以使用已棄用的方法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();
您也可以類似地將此Node
用作目標。
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));
它會按您的預期工作。
JavaFX 無法根據鼠標事件的位置確定單擊了哪個組件。 您必須明確傳遞事件的目標。 在這種情況下,它將是 bluePane。
您可以在此處找到更多信息: https : //docs.oracle.com/javafx/2/events/processing.htm
如果我理解正確,您想要獲取 YellowPane 的子項,並觸發它們的每個事件。
我是這樣完成的
其中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));
}
}
這將抓住yellowPane
每個孩子,你說可能有很多,並觸發他們的每個事件。 希望這是你想要的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.