gpt4 book ai didi

java - 如何在 JavaFX 中以编程方式触发鼠标事件?

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:22:33 27 4
gpt4 key购买 nike

以下代码显示了两个面板,黄色和蓝色,其中蓝色是黄色的子级。

如果我点击蓝色面板的中心,两个面板都会处理鼠标事件。

如何以编程方式模拟相同的行为?

Fire event 按钮中,我尝试这样做,但失败了:生成的事件似乎仅由黄色 Pane 处理。

是否可以发布事件,以便所有 child 都处理它,就好像它发生在“本地”事件中一样?

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(因为它是“最上面的”节点 视觉上),在后一种情况下,目标是 yellowPane

因此,当路由构造发生时(请引用 Event Delivery Process section )在第一种情况下路由将是 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));

或者您可以使用 机器人 来生成点击:

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 方式,现在的问题是如何确定位置上的“最上层”节点

这是不可能以干净的方式实现的一件事。已经有一个feature request关于这个。

我在 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();

您也可以类似地使用此节点作为目标。

关于java - 如何在 JavaFX 中以编程方式触发鼠标事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40041625/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com