2013-06-01 55 views
14

在java文檔中,它對setMouseTransparent表示影響所有孩子以及父母。JavaFX通過透明節點將MouseEvents傳遞給兒童

如何做到這一點,只有父母的透明區域(可以看到它下面的其他節點,但沒有響應鼠標事件)對鼠標事件透明,以便它下面的節點可以接收它們。

在同一窗格中堆疊兩個XYCharts時發生這種情況。只有最後一個可以接收事件。

回答

17

設置pickOnBounds爲相關節點爲false,然後單擊節點中的透明區域將不會註冊該節點的點擊。

定義在由MouseEvent或contains函數調用觸發時,如何爲此節點完成揀選計算。如果pickOnBounds爲true,則通過與此節點的邊界相交來計算拾取,否則,拾取通過與此節點的幾何形狀相交來計算。

樣本輸出

該樣本實際上是遠遠複雜得多,需要證明pickOnBounds功能 - 但我只是做了這個複雜的,這樣它顯示堆疊兩個XYCharts時會發生什麼」相同的窗格「,就像海報的問題中提到的那樣。

在下面的示例中,兩個折線圖堆疊在一起,鼠標移動到一個圖表中,該圖表具有連接到它的mouseenter事件的輝光功能。然後將鼠標移出第一個折線圖數據,並從中刪除輝光。然後將鼠標置於底層堆積圖的第二個折線圖數據上,並將輝光添加到底層堆積圖中的折線圖。

本示例使用Java8開發,描述的着色和行爲是我在Mac OS X和Java 8b91上運行該程序的經驗。

mouseoverline1mouseoverline2

示例代碼

下面的代碼是隻是爲了證明pickOnBounds做工作讓你通過堆放在不透明的節點形狀的頂部透明區域通過鼠標事件。對於圖表中的樣式行來說,這不是推薦的代碼實踐(您最好使用樣式表而不是查找),但也可以不使用折線圖堆棧來在單個圖表上獲取多個系列 - 它只是做這些事情來證明這個答案的選擇範圍概念應用程序是必要的或者更簡單的。

請注意遞歸調用以在圖表顯示在舞臺上以及創建所有必需的節點後爲圖表設置pickOnBounds屬性。

示例代碼的JavaFX 2 XYChart.Series and setOnMouseEntered的適應:

import javafx.application.Application; 
import javafx.collections.*; 
import javafx.event.EventHandler; 
import javafx.scene.*; 
import javafx.scene.chart.*; 
import javafx.scene.effect.Glow; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.StackPane; 
import javafx.scene.shape.Path; 
import javafx.stage.Stage; 

public class LineChartSample extends Application { 
    @SuppressWarnings("unchecked") 
    @Override public void start(Stage stage) { 
    // initialize data 
    ObservableList<XYChart.Data> data = FXCollections.observableArrayList(
     new XYChart.Data(1, 23),new XYChart.Data(2, 14),new XYChart.Data(3, 15),new XYChart.Data(4, 24),new XYChart.Data(5, 34),new XYChart.Data(6, 36),new XYChart.Data(7, 22),new XYChart.Data(8, 45),new XYChart.Data(9, 43),new XYChart.Data(10, 17),new XYChart.Data(11, 29),new XYChart.Data(12, 25) 
    ); 
    ObservableList<XYChart.Data> reversedData = FXCollections.observableArrayList(
     new XYChart.Data(1, 25), new XYChart.Data(2, 29), new XYChart.Data(3, 17), new XYChart.Data(4, 43), new XYChart.Data(5, 45), new XYChart.Data(6, 22), new XYChart.Data(7, 36), new XYChart.Data(8, 34), new XYChart.Data(9, 24), new XYChart.Data(10, 15), new XYChart.Data(11, 14), new XYChart.Data(12, 23) 
    ); 

    // create charts 
    final LineChart<Number, Number> lineChart  = createChart(data); 
    final LineChart<Number, Number> reverseLineChart = createChart(reversedData); 
    StackPane layout = new StackPane(); 
    layout.getChildren().setAll(
     lineChart, 
     reverseLineChart 
    ); 

    // show the scene. 
    Scene scene = new Scene(layout, 800, 600); 
    stage.setScene(scene); 
    stage.show(); 

    // make one line chart line green so it is easy to see which is which. 
    reverseLineChart.lookup(".default-color0.chart-series-line").setStyle("-fx-stroke: forestgreen;"); 

    // turn off pick on bounds for the charts so that clicks only register when you click on shapes. 
    turnOffPickOnBoundsFor(lineChart); 
    turnOffPickOnBoundsFor(reverseLineChart); 

    // add a glow when you mouse over the lines in the line chart so that you can see that they are chosen. 
    addGlowOnMouseOverData(lineChart); 
    addGlowOnMouseOverData(reverseLineChart); 
    } 

    @SuppressWarnings("unchecked") 
    private void turnOffPickOnBoundsFor(Node n) { 
    n.setPickOnBounds(false); 
    if (n instanceof Parent) { 
     for (Node c: ((Parent) n).getChildrenUnmodifiable()) { 
     turnOffPickOnBoundsFor(c); 
     } 
    } 
    } 

    private void addGlowOnMouseOverData(LineChart<Number, Number> lineChart) { 
    // make the first series in the chart glow when you mouse over it. 
    Node n = lineChart.lookup(".chart-series-line.series0"); 
    if (n != null && n instanceof Path) { 
     final Path path = (Path) n; 
     final Glow glow = new Glow(.8); 
     path.setEffect(null); 
     path.setOnMouseEntered(new EventHandler<MouseEvent>() { 
     @Override public void handle(MouseEvent e) { 
      path.setEffect(glow); 
     } 
     }); 
     path.setOnMouseExited(new EventHandler<MouseEvent>() { 
     @Override public void handle(MouseEvent e) { 
      path.setEffect(null); 
     } 
     }); 
    } 
    } 

    private LineChart<Number, Number> createChart(ObservableList<XYChart.Data> data) { 
    final NumberAxis xAxis = new NumberAxis(); 
    final NumberAxis yAxis = new NumberAxis(); 
    xAxis.setLabel("Number of Month"); 
    final LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis); 
    lineChart.setTitle("Stock Monitoring, 2010"); 
    XYChart.Series series = new XYChart.Series(data); 
    series.setName("My portfolio"); 
    series.getData().addAll(); 
    lineChart.getData().add(series); 
    lineChart.setCreateSymbols(false); 
    lineChart.setLegendVisible(false); 
    return lineChart; 
    } 

    public static void main(String[] args) { launch(args); } 
} 
+0

這是我最初嘗試,但設置chart.pickOnBounds(false)沒有達到預期的結果。 – BAR

+0

@ user417896我的解決方案確實有效。我更新了我的答案以證明這一點。也許你只是爲圖表設置pickOnBounds爲false,而不是爲圖表的後代設置。 – jewelsea

+0

有兩點,你的代碼不會像你的圖像那樣給我橙線。在創建添加數據和顯示場景的圖表後,我打電話給turnOffPickOnBounds()。它有同樣的問題。 – BAR

2

而不是做這個的:

// turn off pick on bounds for the charts so that clicks only register when you click on shapes. 
turnOffPickOnBoundsFor(lineChart); 
turnOffPickOnBoundsFor(reverseLineChart); 

做到這一點:

// turn off pick on bounds for the charts so that clicks only register when you click on shapes. 
turnOffPickOnBoundsFor(reverseLineChart, false); 

與folling方法。你

private boolean turnOffPickOnBoundsFor(Node n, boolean plotContent) { 
    boolean result = false; 
    boolean plotContentFound = false; 
    n.setPickOnBounds(false); 
    if(!plotContent){ 
     if(containsStyle(n)){ 
      plotContentFound = true; 
      result=true; 
     } 
     if (n instanceof Parent) { 
      for (Node c : ((Parent) n).getChildrenUnmodifiable()) { 
       if(turnOffPickOnBoundsFor(c,plotContentFound)){ 
        result = true; 
       } 
      } 
     } 
     n.setMouseTransparent(!result); 
    } 
    return result; 
} 

private boolean containsStyle(Node node){ 
    boolean result = false; 
    for (String object : node.getStyleClass()) { 
     if(object.equals("plot-content")){ 
      result = true; 
      break; 
     }     
    } 
    return result; 
} 

還需要使圖表在前面(reverseLineChart)透明。

0

發佈在jewelsea answer中的代碼不起作用。爲了使其工作,我實施了建議的變化是答案和Julia Grabovska評論。
這裏是未來的讀者着想的工作版本:

import javafx.application.Application; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.event.EventHandler; 
import javafx.scene.Node; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.chart.LineChart; 
import javafx.scene.chart.NumberAxis; 
import javafx.scene.chart.XYChart; 
import javafx.scene.effect.Glow; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.StackPane; 
import javafx.scene.shape.Path; 
import javafx.stage.Stage; 

public class LineChartSample extends Application { 

    @SuppressWarnings("unchecked") 
    @Override public void start(Stage stage) { 
     // initialize data 
     ObservableList<XYChart.Data> data = FXCollections.observableArrayList(
       new XYChart.Data(1, 23),new XYChart.Data(2, 14),new XYChart.Data(3, 15),new XYChart.Data(4, 24),new XYChart.Data(5, 34),new XYChart.Data(6, 36),new XYChart.Data(7, 22),new XYChart.Data(8, 45),new XYChart.Data(9, 43),new XYChart.Data(10, 17),new XYChart.Data(11, 29),new XYChart.Data(12, 25) 
       ); 
     ObservableList<XYChart.Data> reversedData = FXCollections.observableArrayList(
       new XYChart.Data(1, 25), new XYChart.Data(2, 29), new XYChart.Data(3, 17), new XYChart.Data(4, 43), new XYChart.Data(5, 45), new XYChart.Data(6, 22), new XYChart.Data(7, 36), new XYChart.Data(8, 34), new XYChart.Data(9, 24), new XYChart.Data(10, 15), new XYChart.Data(11, 14), new XYChart.Data(12, 23) 
       ); 

     // create charts 
     final LineChart<Number, Number> bottomLineChart = createChart(data); 
     final LineChart<Number, Number> topLineChart  = createChart(reversedData); 

     //add css to make top chart line transparent as pointed out by Julia Grabovska 
     //and user1638436, as well as make line green 
     topLineChart.getStylesheets().add(getClass().getResource("LineChartSample.css").toExternalForm()); 

     StackPane layout = new StackPane(bottomLineChart, topLineChart); 

     // show the scene. 
     Scene scene = new Scene(layout, 800, 600); 
     stage.setScene(scene); 
     stage.show(); 

     // turn off pick on bounds for the charts so that clicks only register when you click on shapes. 
     turnOffPickOnBoundsFor(topLineChart, false); //taken from user1638436 answer 

     // add a glow when you mouse over the lines in the line chart so that you can see that they are chosen. 
     addGlowOnMouseOverData(bottomLineChart); 
     addGlowOnMouseOverData(topLineChart); 
    } 

    //taken from user1638436 answer (https://stackoverflow.com/a/18104172/3992939) 
    private boolean turnOffPickOnBoundsFor(Node n, boolean plotContent) { 
     boolean result = false; 
     boolean plotContentFound = false; 
     n.setPickOnBounds(false); 
     if(!plotContent){ 
      if(containsPlotContent(n)){ 
       plotContentFound = true; 
       result=true; 
      } 
      if (n instanceof Parent) { 
       for (Node c : ((Parent) n).getChildrenUnmodifiable()) { 
        if(turnOffPickOnBoundsFor(c,plotContentFound)){ 
         result = true; 
        } 
       } 
      } 
      n.setMouseTransparent(!result); 
     } 
     return result; 
    } 

    private boolean containsPlotContent(Node node){ 
     boolean result = false; 
     for (String object : node.getStyleClass()) { 
      if(object.equals("plot-content")){ 
       result = true; 
       break; 
      } 
     } 
     return result; 
    } 

    private void addGlowOnMouseOverData(LineChart<Number, Number> lineChart) { 
     // make the first series in the chart glow when you mouse over it. 
     Node n = lineChart.lookup(".chart-series-line.series0"); 
     if ((n != null) && (n instanceof Path)) { 
      final Path path = (Path) n; 
      final Glow glow = new Glow(.8); 
      path.setEffect(null); 
      path.setOnMouseEntered(new EventHandler<MouseEvent>() { 
       @Override public void handle(MouseEvent e) { 
        path.setEffect(glow); 
       } 
      }); 
      path.setOnMouseExited(new EventHandler<MouseEvent>() { 
       @Override public void handle(MouseEvent e) { 
        path.setEffect(null); 
       } 
      }); 
     } 
    } 

    private LineChart<Number, Number> createChart(ObservableList<XYChart.Data> data) { 
     final NumberAxis xAxis = new NumberAxis(); 
     final NumberAxis yAxis = new NumberAxis(); 
     xAxis.setLabel("Number of Month"); 
     final LineChart<Number, Number> lineChart = new LineChart<>(xAxis, yAxis); 
     lineChart.setTitle("Stock Monitoring, 2010"); 
     XYChart.Series series = new XYChart.Series(data); 
     series.setName("My portfolio"); 
     series.getData().addAll(); 
     lineChart.getData().add(series); 
     lineChart.setCreateSymbols(false); 
     lineChart.setLegendVisible(false); 
     return lineChart; 
    } 

    public static void main(String[] args) { launch(args); } 
} 

LineChartSample.css:

.chart-plot-background { 
    -fx-background-color:transparent; 
} 
.default-color0.chart-series-line{ 
    -fx-stroke: forestgreen; 
} 

turnOffPickOnBoundsFor方法的一個簡化版本:

private boolean turnOffPickOnBoundsFor(Node n) { 

    n.setPickOnBounds(false); 

    boolean isContainPlotContent = containsPlotContent(n); 

    if (! isContainPlotContent && (n instanceof Parent)) { 

     for (Node c : ((Parent) n).getChildrenUnmodifiable()) { 

      if(turnOffPickOnBoundsFor(c)){ 
       isContainPlotContent = true; 
      } 
     } 
    } 

    n.setMouseTransparent(!isContainPlotContent); 
    return isContainPlotContent; 
} 
0

基於jewelsea答案here將窗格的頂部窗格背景顏色設置爲nulltopPane.setPickOnBounds(false);正常工作:

import javafx.application.Application; 
import javafx.event.EventHandler; 
import javafx.scene.Cursor; 
import javafx.scene.Node; 
import javafx.scene.Scene; 
import javafx.scene.control.Label; 
import javafx.scene.input.MouseEvent; 
import javafx.scene.layout.Pane; 
import javafx.scene.layout.StackPane; 
import javafx.stage.Stage; 


public class PropagateEvents extends Application { 

    private double x, y; 

    @Override public void start(Stage primaryStage) throws Exception { 

     StackPane root = new StackPane(getBottomPane(), getTopPane()); 
     Scene scene = new Scene(root); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    private Pane getBottomPane() { 

     Pane pane = new Pane(); 
     pane.setStyle("-fx-background-color : yellow;"); 
     pane.setPrefSize(250,200); 
     pane.setOnMouseClicked(e-> System.out.println("Bottom pane recieved click event")); 
     return pane; 
    } 

    private Pane getTopPane() { 

     Label label = new Label(); 
     label.setPrefSize(20,10); 
     label.setStyle("-fx-background-color:red;"); 
     label.layoutXProperty().setValue(30); label.layoutYProperty().setValue(30); 
     addDragSupport(label); 

     Pane pane = new Pane(label); 
     // NULL color setPickOnBounds do the trick 
     pane.setPickOnBounds(false); 
     pane.setStyle("-fx-background-color: null; "); 

     return pane; 
    } 
    //drag support for red label 
    private void addDragSupport(Node node) { 

     node.setOnMousePressed(new EventHandler<MouseEvent>() { 
      @Override public void handle(MouseEvent mouseEvent) { 
       x = node.getLayoutX() - mouseEvent.getSceneX(); 
       y = node.getLayoutY() - mouseEvent.getSceneY(); 
       node.setCursor(Cursor.MOVE); 
      } 
     }); 
     node.setOnMouseReleased(new EventHandler<MouseEvent>() { 
      @Override public void handle(MouseEvent mouseEvent) { 
       node.setCursor(Cursor.HAND); 
      } 
     }); 
     node.setOnMouseDragged(new EventHandler<MouseEvent>() { 
      @Override public void handle(MouseEvent mouseEvent) { 
       node.setLayoutX(mouseEvent.getSceneX() + x); 
       node.setLayoutY(mouseEvent.getSceneY() + y); 
      } 
     }); 
     node.setOnMouseEntered(new EventHandler<MouseEvent>() { 
      @Override public void handle(MouseEvent mouseEvent) { 
       node.setCursor(Cursor.HAND); 
      } 
     }); 
    } 

    public static void main (String[] args) {launch(null); } 
}