How to programmatically trigger mouse events in JavaFX?
The following code shows two panels, yellow and blue, where blue is the child of yellow
If I click the center of the blue panel, two panels will be used to handle mouse events
How do I programmatically simulate the same behavior?
In the fire event button, I tried to do this, but failed: the generated event appears to be handled only by the Yellow pane
Can events be published so that all children can handle "native" events as well?
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,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)); 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,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); } }@H_404_20@UPDATE
我不能明确提到子节点,因为一般情况下它可以是无数个子节点.
我怎么知道要瞄准哪一个?
我想传递坐标,让系统自动确定目标.
Solution
The difference between the actual click and the program click is that in the first case, the original event target is bluepane (because it is the visual "top" node), and in the latter case, the target is yellowpane
Therefore, when the route construction occurs (refer to the event delivery process section), the route will be root – > yellowpane – > bluepane. In the second case, it is only root – > yellowpane, so the bluepane is not affected by the event
Based on this, you can locate bluepane:
Event.fireEvent(bluePane,null));@H_404_20@或者您可以使用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(); }@H_404_20@更新1:
如果你想坚持使用JavaFX方式,现在的问题是如何确定位置上的“最上面”节点.
这是一件以干净的方式无法实现的事情.关于此已经有了feature request.
我在fxexperience上的辅助方法上找到了这个,可用于确定某个位置上的节点:
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,true /* rootScene */); if (child.isVisible() && !child.isMouseTransparent() && child.contains(p)) { bestMatchingChild = child; break; } } if (bestMatchingChild != null) { return pick(bestMatchingChild,sceneX,sceneY); } } return node; }@H_404_20@您可以使用如下:
Node pick = pick(root,sceneCoords.getY()); Event.fireEvent(pick,null));@H_404_20@UPDATE2:
您还可以使用不推荐使用的方法Node.impl_pickNode(PickRay pickRay,PickResultChooser结果)来获取场景位置上的相交节点:
PickRay pickRay = new PickRay((int) sceneCoords.getX(),(int) sceneCoords.getY(),1.0,1.0); PickResultChooser pickResultChooser = new PickResultChooser(); root.impl_pickNode(pickRay,pickResultChooser); Node intersectedNode = pickResultChooser.getIntersectedNode();@H_404_20@您也可以同样使用此节点作为目标.