JavaFX在两个图表中打印相同的XYChart.Series

时间:2018-04-11 08:41:35

标签: java javafx charts

从这个问题JavaFX LineChart Hover Values开始,我在第一个答案https://gist.github.com/jewelsea/4681797中将Gist修改为此

public class TestCharts extends Application
{

    ObservableList<XYChart.Data<Integer, Integer>> _serie;

    @SuppressWarnings("unchecked")
    @Override
    public void start(Stage stage) {

        LineChart lineChart = new LineChart(new NumberAxis(), new NumberAxis());

        XYChart.Series serie = new XYChart.Series(
            "Test",
            getTestValues()
        );

        lineChart.getData().add(serie);
        lineChart.setCursor(Cursor.CROSSHAIR);
        lineChart.setTitle("First Chart");

        stage.setTitle("First Chart");
        stage.setScene(new Scene(lineChart, 500, 400));
        stage.show();

        secondHandChart();
    }

    private void secondHandChart() {
        LineChart lineChart = new LineChart(new NumberAxis(), new NumberAxis());
        XYChart.Series serie = new XYChart.Series(
            "Test",
            getTestValues()
        );

        lineChart.getData().add(serie);

        lineChart.setCursor(Cursor.CROSSHAIR);

        lineChart.setTitle("Second Chart");
        Stage stage = new Stage();
        stage.setTitle("Second Chart");
        stage.setScene(new Scene(lineChart, 500, 400));
        stage.show();
    }

    private ObservableList<XYChart.Data<Integer, Integer>> getTestValues() {
        return plot(23, 14, 15, 24, 34, 36, 22, 45, 43, 17, 29, 25);
    }

    public ObservableList<XYChart.Data<Integer, Integer>> plot(int... y) {
        if (_serie == null) {
            _serie = FXCollections.observableArrayList();
            int i = 0;
            while (i < y.length) {
                XYChart.Data<Integer, Integer> data = new XYChart.Data<>(i + 1, y[i]);
                data.setNode(
                    new HoveredThresholdNode(
                        (i == 0) ? 0 : y[i - 1],
                        y[i]
                    )
                );

                _serie.add(data);
                i++;
            }
            return _serie;
        }
        else {
            return FXCollections.observableArrayList(_serie);
        }
    }

    /**
     * a node which displays a value on hover, but is otherwise empty
     */
    class HoveredThresholdNode extends StackPane
    {
        HoveredThresholdNode(int priorValue, int value) {
            setPrefSize(8, 8);

            final Label label = createDataThresholdLabel(priorValue, value);

            setOnMouseEntered(new EventHandler<MouseEvent>()
            {
                @Override
                public void handle(MouseEvent mouseEvent) {
                    getChildren().setAll(label);
                    setCursor(Cursor.NONE);
                    toFront();
                }
            });
            setOnMouseExited(new EventHandler<MouseEvent>()
            {
                @Override
                public void handle(MouseEvent mouseEvent) {
                    getChildren().clear();
                    setCursor(Cursor.CROSSHAIR);
                }
            });
        }

        private Label createDataThresholdLabel(int priorValue, int value) {
            final Label label = new Label(value + "");
            label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");

            label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
            return label;
        }
    }

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

这门课唯一能够表达我的问题。我想获得同一系列的两个图表,getTestValues()。问题是:第二个图表是完美的(2°Img),但第一个图表(1°Img)缺少HoveredThresholdNode,当我调整第一个图表的大小时,第二个图表中的HoveredThresholdNodes正在改变位置(3°Image)。如何获得系列的副本,还包含这些节点的副本? 我知道打印相同的系列是没有意义的,但实际上我有两个系列列表,我有一个每个图表和一个图表,两个列表一起用于计算pourposes。无论如何,问题可以简化为两个图表中的一个系列。

enter image description here enter image description here enter image description here

1 个答案:

答案 0 :(得分:2)

XYChart.Data不能跨图表共享,因为它有一个添加到图表中的节点。

要在图表之间共享数据,您必须

  • 实现仅包含值
  • 的自定义数据类
  • 保留共享数据列表
  • 对于每个图表,创建由共享数据支持的XYChart.Data
  • 管理图表数据与支持数据的同步(请注意,下面的简单示例未完成更新列表修改的图表数据)

一个简单的例子:

@SuppressWarnings({ "unchecked", "rawtypes" })
public class TestChart extends Application {

    // the shared x/y data pairs
    private ObservableList<XYData<Integer, Integer>> backingData;

    /**
     * Custom xy data class which exposes its values as properties and nothing
     * else.
     */
    public static class XYData<X, Y> {
        private ObjectProperty<X> xValue;

        private ObjectProperty<Y> yValue;

        public XYData(X xValue, Y yValue) {
            this.xValue = new SimpleObjectProperty<>(this, "xValue", xValue);
            this.yValue = new SimpleObjectProperty<>(this, "yValue", yValue);
        }

        public ObjectProperty<X> xValueProperty() {
            return xValue;
        }

        public void setXValue(X value) {
            xValueProperty().set(value);
        }

        public X getXValue() {
            return xValueProperty().get();
        }

        public ObjectProperty<Y> yValueProperty() {
            return yValue;
        }

        public void setYValue(Y value) {
            yValueProperty().set(value);
        }

        public Y getYValue() {
            return yValueProperty().get();
        }
    }

    @Override
    public void start(Stage stage) {
        if (backingData == null) {
            backingData = createBackingData();
        }

       LineChart lineChart = new LineChart(new NumberAxis(), new NumberAxis());

        XYChart.Series serie = new XYChart.Series("Test", createChartData());

        lineChart.getData().add(serie);
        lineChart.setCursor(Cursor.CROSSHAIR);
        lineChart.setTitle("First Chart");

        stage.setTitle("First Chart");
        stage.setScene(new Scene(lineChart, 500, 400));
        stage.show();

        secondHandChart();
    }

    private void secondHandChart() {
        LineChart lineChart = new LineChart(new NumberAxis(), new NumberAxis());
        XYChart.Series serie = new XYChart.Series("Test", createChartData());

        lineChart.getData().add(serie);

        lineChart.setCursor(Cursor.CROSSHAIR);

        lineChart.setTitle("Second Chart");
        Stage stage = new Stage();
        stage.setTitle("Second Chart");
        stage.setScene(new Scene(lineChart, 500, 400));
        stage.show();
    }

    private ObservableList<XYData<Integer, Integer>> createBackingData() {
        ObservableList<XYData<Integer, Integer>> data = FXCollections
                .observableArrayList();
        Integer[] values = { 23, 14, 15, 24, 34, 36, 22, 45, 43, 17, 29, 25 };
        for (int i = 0; i < values.length; i++) {
            data.add(new XYData(i, values[i]));
        }
        return data;
    }

    private ObservableList<XYChart.Data<Integer, Integer>> createChartData() {
        ObservableList<XYChart.Data<Integer, Integer>> chartData = FXCollections
                .observableArrayList();
        for (int i = 0; i < backingData.size(); i++) {
            XYData<Integer, Integer> b = backingData.get(i);
            XYChart.Data<Integer, Integer> cd = new XYChart.Data<>(
                    b.getXValue(), b.getYValue());
            cd.XValueProperty().bind(b.xValueProperty());
            cd.YValueProperty().bind(b.yValueProperty());
            cd.setNode(new HoveredThresholdNode(
                    (i == 0) ? 0 : backingData.get(i - 1).getYValue(),
                    backingData.get(i).getYValue()));
            chartData.add(cd);
        }
        return chartData;
    }

    /**
     * a node which displays a value on hover, but is otherwise empty
     */
    class HoveredThresholdNode extends StackPane {
        HoveredThresholdNode(int priorValue, int value) {
            setPrefSize(8, 8);

            final Label label = createDataThresholdLabel(priorValue, value);

            setOnMouseEntered(new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent mouseEvent) {
                    getChildren().setAll(label);
                    setCursor(Cursor.NONE);
                    toFront();
                }
            });
            setOnMouseExited(new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent mouseEvent) {
                    getChildren().clear();
                    setCursor(Cursor.CROSSHAIR);
                }
            });
        }

        private Label createDataThresholdLabel(int priorValue, int value) {
            final Label label = new Label(value + "");
            label.setStyle("-fx-font-size: 20; -fx-font-weight: bold;");

            label.setMinSize(Label.USE_PREF_SIZE, Label.USE_PREF_SIZE);
            return label;
        }
    }

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

}