2016-10-14 92 views
2

下面的代码显示两个面板,黄色和蓝色,其中蓝色是黄色的孩子。如何在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); 
    } 

} 

UPDATE

我不能提子节点明确,因为在一般情况下,它可儿的众多数量。

我怎么知道哪一个目标?

我想传递坐标并让系统自动确定目标。

+0

是不是有点相关[你昨天问过的问题](http://stackoverflow.com/q/40023260/3429133)? – beatngu13

回答

2

您的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

4

实际(phisycal)点击和自己的程序点击之间的区别是,在第一种情况下原始事件的目标是bluePane(因为它是“最上“Node),在后一种情况下,目标是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)); 

或者你可以使用一个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

这是一件不可能以干净的方式。关于这一点已经有feature request了。

我发现这对fxexperience辅助方法可以用来determinte的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)); 

Update2:

,您还可以使用的方法已过时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为目标。

+0

我只得到参数'Event.fireEvent(EventTarget eventTarget,Event event);'。我最近使用了另一组javaFX.event.Event的参数,就像在这个答案中一样。任何想法发生了什么?为什么我不能使用'Event.fireEvent'(EventType <?extends MouseEvent> eventType,double x,double y' ... – Lealo

+1

'Event.fireEvent'有两个参数:第一个是'EventTarget'('pick',它是一个实现了'EventTarget'的节点),第二个是'Event'(它是'new MouseEvent(...)')。 – DVarga

+0

啊,我把'new MouseEvent(...)'构造函数参数作为Event.fireEvent()方法的参数,谢谢 – Lealo

0

如果我正确地理解了你,你想让yellowPane的孩子们,并发射他们的每一个事件。

我做到了这一点,像这样

ObservableList<Node> nodesyellowPane.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每一个孩子,你说可能是很多,和火他们的每一个事件。希望这是你想要的。

相关问题