2016-10-14 141 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每一個孩子,你說可能是很多,和火他們的每一個事件。希望這是你想要的。

相關問題