简体   繁体   中英

JavaFx and binding XYChart.Series

I have some problem with updating my Chart

I have class Chart

public class Chart {
    public LineChart<String, Number> createChart() {
        // defining the axes
        final CategoryAxis xAxis = new CategoryAxis(); // we are gonna plot against time
        final NumberAxis yAxis = new NumberAxis();
        xAxis.setLabel("Time/s");
        xAxis.setAnimated(false); // axis animations are removed
        yAxis.setLabel("Value");
        yAxis.setAnimated(false); // axis animations are removed

        // creating the line chart with two axis created above
        final LineChart<String, Number> lineChart = new LineChart<>(xAxis, yAxis);
        lineChart.setAnimated(false); // disable animations

        // defining a series to display data
        XYChart.Series<String, Number> series = new XYChart.Series<>();
        series.setName("Data Series");

        // add series to chart
        lineChart.getData().add(series);

        return lineChart;
    }
}

And main class

public class Main extends Application {
    final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
    private Service service;

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


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

        primaryStage.setTitle("Hello World");
        primaryStage.setWidth(500);
        primaryStage.setHeight(500);

        Chart chart = new Chart();
        LineChart<String, Number> lineChart = chart.createChart();
        //   setup scene
        Scene scene = new Scene(lineChart, 800, 600);
        primaryStage.setScene(scene);

        createService();

        primaryStage.show();
        startService();

// First variant
Platform.runLater(() -> lineChart.getData().get(0).getData().add((XYChart.Data<String, Number>) service.valueProperty().getValue()));

//Second variant
lineChart.getData().get(0).getData().add((XYChart.Data<String, Number>) service.valueProperty().getValue());

//Third variant
lineChart.getData().get(0).dataProperty().bind(service.valueProperty());

    }

    private void createService() {

        service = new Service<ObservableList<XYChart.Data<String, Number>>>() {
            @Override
            protected Task<ObservableList<XYChart.Data<String, Number>>> createTask() {
                return new Task<ObservableList<XYChart.Data<String, Number>>>() {
                    Date now;
                    Integer random;

                    @Override
                    protected ObservableList<XYChart.Data<String, Number>> call() throws InterruptedException {
                        for (int i = 0; i < 10; i++) {
                            now = new Date();
                            random = ThreadLocalRandom.current().nextInt(10);

                            System.out.println(simpleDateFormat.format(now) + "  " + random);
                            updateValue(FXCollections.observableArrayList(new XYChart.Data(simpleDateFormat.format(now), random)));
                            Thread.sleep(500);
                        }
                        return FXCollections.observableArrayList(new XYChart.Data(simpleDateFormat.format(now), random));
                    }
                };
            }
        };


    }

    private void startService() {
        if (!service.isRunning()) {
            service.reset();
            service.start();
        }
    }
}

The first and second variants from Main class throw exception Exception in thread "JavaFX Application Thread" java.lang.NullPointerException and doesn't update my Chart, but the third one updates it but it update, but I need to add new value to my graphic

How can I update my code to add all 10 values into graph instead only 1 or only last one?

I had to change my Main class to

public class Main extends Application {
    final SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss");
    private LineChart<String, Number> lineChart;

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


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

        primaryStage.setTitle("Hello World");
        primaryStage.setWidth(500);
        primaryStage.setHeight(500);

        Chart chart = new Chart();
        lineChart = chart.createChart();
        //   setup scene
        Scene scene = new Scene(lineChart, 800, 600);
        primaryStage.setScene(scene);

        createService();

        primaryStage.show();

    }

    private void createService() {

        Task<Void> task = new Task<Void>() {
            Date now;
            Integer random;

            @Override
            protected Void call() throws InterruptedException {
                for (int i = 0; i < 10; i++) {
                    now = new Date();
                    random = ThreadLocalRandom.current().nextInt(10);

                    System.out.println(simpleDateFormat.format(now) + "  " + random);
                    Platform.runLater(() -> updateChart(now, random));
                    Thread.sleep(500);
                }
                return null;
            }
        };
        Thread thread = new Thread(task);
        thread.setDaemon(true);
        thread.start();
    }

    private void updateChart(Date now, Integer random) {
        lineChart.getData().get(0).getData().add(new XYChart.Data(simpleDateFormat.format(now), random));
    }

}
  1. First variant

valueProperty.getValue() might be null since service task execution might not reach setValue call yet. But most important thing is that this cast

Platform.runLater(() -> lineChart.getData().get(0).getData().add(/*here -->*/(XYChart.Data<String, Number>) service.valueProperty().getValue()));

does not make sense. Service valueProperty type is ObservableList<XYChart.Data<String, Number>> as specified in line

service = new Service<ObservableList<XYChart.Data<String, Number>>>() 

If you wanted to add current service value you'd rather should use ObservableList::addAll method and cast value to list type:

Platform.runLater(() -> lineChart.getData().get(0).getData().addAll((ObservableList<XYChart.Data<String, Number>>)service.valueProperty().getValue()));

Also since you know what type of service you'd have later you can already specify its full type in declaration by changing:

private Service service;

into

private Service<ObservableList<XYChart.Data<String, Number>>> service;

resulting in just:

Platform.runLater(() -> lineChart.getData().get(0).getData().addAll(service.getValue()));

  1. Second variant

Basically same story as for first variant but additionaly you make changes not on the UI thread so this might cause troubles when updating chart.


  1. Thrid variant

You overwrite service value with list containing only one element:

// each time new list!
updateValue(FXCollections.observableArrayList(new XYChart.Data(simpleDateFormat.format(now), random)));

In the service definition you should create one list and append points to that list

private void createService() {
    service = new Service<ObservableList<XYChart.Data<String, Number>>>() {
        @Override
        protected Task<ObservableList<XYChart.Data<String, Number>>> createTask() {
            return new Task<ObservableList<XYChart.Data<String, Number>>>() {
                Date now;
                Integer random;
                // Specify ONE list
                ObservableList<XYChart.Data<String, Number>> data = FXCollections.observableArrayList();
                @Override
                protected ObservableList<XYChart.Data<String, Number>> call() throws InterruptedException {
                    updateValue(data);
                    for (int i = 0; i < 10; i++) {
                        now = new Date();
                        random = ThreadLocalRandom.current().nextInt(10);
                        System.out.println(simpleDateFormat.format(now) + "  " + random);
                        // Add points to the data (on UI thread to have chart updated properly!)
                        Platform.runLater(() -> data.add(new XYChart.Data(simpleDateFormat.format(now), random)));
                        Thread.sleep(500);
                    }
                    return data;
                }
            };
        }
    };
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM