2013-06-01 1 views
14

В java-документе говорится, что для setMouseTransparent это касается всех детей, а также родителя.JavaFX Pass MouseEvents через прозрачный узел для детей

Как это можно сделать, так что прозрачные области родителя (могут видеть другие узлы ниже него, но не реагировать на события мыши) прозрачны для событий мыши, так что узлы под ним могут их получать.

Это происходит при складывании двух XYCharts в одной и той же панели. Только последние могут принимать события.

ответ

17

Установить pickOnBounds для соответствующих узлов в false, а затем щелкнуть по прозрачным областям в узле не будет регистрировать щелчок с этим узлом.

Определяет, как вычисление вычислений выполняется для этого узла при запуске MouseEvent или содержит вызов функции. Если pickOnBounds истинно, то выбор вычисляется путем пересечения с границами этого узла, иначе сбор вычисляется путем пересечения с геометрической формой этого узла.

Пример вывода

Этот пример на самом деле гораздо сложнее, чем это необходимо, чтобы продемонстрировать функцию 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

@ пользователь417896 мое решение делает работа. Я обновил свой ответ, чтобы продемонстрировать это. Возможно, вы установите для 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); 

с методами феллинг.

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 не работает. Чтобы он работал, я внедрил предложенные изменения: ответ и комментарий Джулии Грабовской.
Вот рабочая версия для будущих читателей:

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 установка верхней панели цвета фона панели на null и topPane.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); } 
} 
Смежные вопросы