JavaFX 将鼠标事件通过透明节点传递给子级

2022-09-01 23:16:13

在java文档中,它说setMouseTransparent会影响所有子级以及父级。

如何使它只有父级的透明区域(可以看到它下面的其他节点,但不响应鼠标事件)对鼠标事件是透明的,以便它下面的节点可以接收它们。

在同一窗格中堆叠两个 XYChart 时,会发生这种情况。只有最后添加的一个可以接收事件。


答案 1

将相关节点的 pickOnBounds 设置为 ,则单击节点中的透明区域将不会向该节点注册单击。false

定义在由 MouseEvent 或包含函数调用触发时如何为此节点完成拾取计算。如果 pickOnBounds 为 true,则通过与此节点的边界相交来计算拣选,否则通过与此节点的几何形状相交来计算拣选。

示例输出

此示例实际上比演示该函数所需的要复杂得多 - 但我刚刚做了一些如此复杂的事情,以便它显示海报问题中提到的“在同一窗格中堆叠两个时”发生的情况。pickOnBoundsXYCharts

在下面的示例中,两个折线图相互堆叠,鼠标移动到一个图表中的数据线上,该图表具有附加到其 mouseenter 事件的发光函数。然后,将鼠标移出第一折线图数据,并从中移除发光。然后将鼠标放在基础堆叠图的第二折线图数据上,并将发光添加到基础堆叠图中的该折线图中。

此示例是使用 Java8 开发的,所描述的着色和行为就是我在 Mac OS X 和 Java 8b91 上运行该程序所执行的。

mouseoverline1mouseoverline2

示例代码

下面的代码仅用于演示,允许您通过堆叠在不透明节点形状之上的透明区域传递鼠标事件。对于图表中的样式化线条,这不是一个推荐的代码实践(最好使用样式表而不是查找),也没有必要使用折线图堆栈在单个图表上获取多个序列 - 只需执行这些操作来演示此答案的边界选择概念应用程序,这是必要或更简单的。pickOnBounds

请注意在某个阶段显示图表并创建其所有必需节点之后,为图表设置属性的递归调用。pickOnBounds

示例代码是 JavaFX 2 XYChart.Series 和 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); }
}

答案 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;
}

此外,您需要使前面的图表(反向行图表)透明。


推荐