如何在JavaFX中以编程方式触发鼠标事件? [英] How to fire mouse event programmatically in JavaFX?

查看:62
本文介绍了如何在JavaFX中以编程方式触发鼠标事件?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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?

Fire event按钮中,我尝试执行此操作,但失败了:生成的事件似乎只由黄色窗格处理.

In the Fire event button I tried to do this, but failed: generated event appeared to handled only by yellow pane.

是否可以发布事件,以便所有子事件都可以处理该事件,就像发生在本机"事件中一样?

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);
   }

}

更新

我不能明确提及子节点,因为在一般情况下,它可以有许多子节点.

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.

推荐答案

实际(物理)点击与程序化点击之间的区别在于,在第一种情况下,原始事件目标是bluePane(因为它是视觉上是最高" Node),在后一种情况下,目标是yellowPane.

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.

因此,在进行路由构建时(请参考事件传递过程"部分),第一种情况下的路由为root-> yellowPane-> bluePane,第二种情况下的路由仅为root-> yellowPane,因此bluePane不受事件影响.

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.

基于此,您可以定位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例如生成点击:

Or you can use a Robot for example to generate the click:

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 .

If you want to stick with the JavaFX way, the question is now how to determine the "uppermost" Node on a position.

这是一件事,用干净的方式是不可能的.关于此的功能请求.

This is one thing that is not possible in a clean way. There is already a feature request about this.

我在 fxexperience 的帮助方法中找到了此方法,用于确定特定位置上的Node:

I have found this on helper method on fxexperience that can be used to determinte the Node on a certain position:

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));


Update2:

您还可以使用不推荐使用的方法 Node.impl_pickNode(PickRay pickRay, PickResultChooser result)在场景位置获取相交的Node:

You can also use the deprecated method Node.impl_pickNode(PickRay pickRay, PickResultChooser result) to get the intersected Node on a scene position:

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用作目标.

You can use also this Node as target similarly.

这篇关于如何在JavaFX中以编程方式触发鼠标事件?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆