【问题标题】:How do you create an animated LineChart in JavaFX?如何在 JavaFX 中创建动画 LineChart?
【发布时间】:2016-08-24 14:38:16
【问题描述】:

我在 SceneBuilder 中创建了一个 LineChart,并正在尝试研究如何将数据写入其中,以便实时绘制。 在我的Controller 课程中,我有:

    public class Controller {    
        ...    
        @FXML private LineChart<Number, Number> chart;
        @FXML private NumberAxis xAxis, yAxis;
        private XYChart.Series<Number, Number> series;
        private boolean run = true;
        ...

        public init() {
            xAxis.setAutoRanging(false);
            xAxis.setTickLabelsVisible(false);
            xAxis.setTickMarkVisible(false);
            xAxis.setMinorTickVisible(false);

            series = new XYChart.Series<Number, Number>();
            series.setName("Data plot"); 

            chart.setAnimated(false);
            chart.getData().addAll(series);           
        }  

        ...
        //When a button is clicked, a signal is sent to my server that starts a 
        //tracking application. Then it enters the while loop. This code
        //then makes requests to the server for data (getData()).
        //This is the data that I would like to display.
        @FXML
        private track() {
            connection.startTracking();

            Platform.runLater(new Runnable() {
                @Override
                public void run() {

                while(run) {
                    int[] data = connection.getData() //from somewhere else
                    dataManager.dataToSeries(data, series);

                    if(!series.getData.isEmpty()) {
                        chart.getData().retainAll();
                        chart.getData.addAll(series);
                    }
                }
            }
       }
  }    

这是来自dataControllerdataToSeries()方法

...
private void dataToSeries(int[] data, XYChart.Series<Number, Number> series) {
    int yValue = 0;
    int len = data.length;

    for (int i = 0; i < len; i++) {
        series.getData().add(new XYChart.Data<Number, Number>(data[i], yValue);  
        yValue++;
    }
 }

如果我打印出Controller 中的系列,我会得到我期望的数据,但是它的格式是:

Series: [Data[11,1,null], Data[16,2,null], Data[21,3,null]

谁能解释为什么我的LineChart 上没有显示这些数据?

【问题讨论】:

  • IDK 如何调用addToChart,但由于它包含一个无限循环,这很容易阻塞 UI 线程,从而阻止用户界面的任何更新。另一方面,如果它在不同的线程上运行,您正在从非应用程序线程更新 UI,这也很糟糕......
  • 抱歉,我会更新我的代码以使其更清晰。我试图省略不相关的代码
  • 我已经包含了对 addToChart() 方法的描述,现在我将其更改为 track()(这是我程序中的实际名称)

标签: java javafx charts scenebuilder


【解决方案1】:

您使用Platform.runLater 在应用程序线程上发布的Runnable 阻塞了UI 线程。

由于track() 方法用@FXML 注释,我怀疑它在fxml 文件中用作EventHandler,因此无论如何都在应用程序线程上运行,这消除了使用Platform.runLater 运行代码的必要性在应用程序线程上。

为了不阻塞 UI 线程,循环应该在非应用程序线程上运行,并且只有 UI 更新应该在应用程序线程上完成。此外,更新之间的小中断可能没有错。在这种情况下,ScheduledExecutorService 可以提供合适的调度:

例如Application代码:

private final ScheduledExecutorService service = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture future;

private Random random = new Random();

private int[] getData() {
    int[] result = new int[10];
    for (int i = 0; i < result.length; i++) {
        result[i] = random.nextInt(10);
    }
    return result;
}

private static void dataToSeries(int[] data, XYChart.Series<Number, Number> series) {
    int len = data.length;

    int size = series.getData().size();
    if (size > len) {
        series.getData().subList(len, series.getData().size()).clear();
    } else if (size < len) {
        for (; size < len; size++) {
            series.getData().add(new XYChart.Data<>(0, size));
        }
    }

    for (int i = 0; i < len; i++) {
        series.getData().get(i).setXValue(data[i]);
    }
}

@Override
public void start(Stage primaryStage) {
    ToggleButton btn = new ToggleButton("updating");
    btn.setSelected(false);

    XYChart.Series<Number, Number> series = new XYChart.Series<>();

    LineChart<Number, Number> chart = new LineChart<>(new NumberAxis(), new NumberAxis(), FXCollections.observableArrayList(series));
    chart.setAnimated(false);

    Runnable dataGetter = () -> {
        try {
            // simulate some delay caused by the io operation
            Thread.sleep(100);
        } catch (InterruptedException ex) {
        }
        int[] data = getData();
        Platform.runLater(() -> {
            // update ui
            dataToSeries(data, series);
        });
    };

    btn.selectedProperty().addListener((a, b, newValue) -> {
        if (newValue) {
            // update every second
            future = service.scheduleWithFixedDelay(dataGetter, 0, 1, TimeUnit.SECONDS);
        } else {
            // stop updates
            future.cancel(true);
            future = null;
        }
    });

    VBox root = new VBox(10, chart, btn);

    Scene scene = new Scene(root);

    primaryStage.setScene(scene);
    primaryStage.show();
}

@Override
public void stop() throws Exception {
    service.shutdownNow();
}

【讨论】:

  • 感谢您的帮助。我想我明白你的意思了。我会尝试将您的建议整合到我的代码中
  • 谢谢。这似乎奏效了。我没有意识到我阻塞了任何线程
【解决方案2】:

基于rootFoder -&gt; jdk.x.x.x -&gt; demo -&gt; javafx_samples中的Java SE Development Demos and Samples Downloads执行Ensemble8.jar。

在应用程序中转到charts 部分并搜索LineChart

你应该看到类似的东西 Image from Ensembre Application

然后点击查看源代码

这是一个实现示例。

@FXML
private LineChart<Number, Number> lineChart;

@FXML
private NumberAxis xAxis;

@FXML
private NumberAxis yAxis;

@Override
public void initialize(URL location, ResourceBundle resources) {
    lineChart.setTitle("Stock Monitoring, 2010");
    ObservableList<XYChart.Series<Number,Number>> dataChart =
            FXCollections.observableArrayList(
                    new LineChart.Series("Serie 1",FXCollections.observableArrayList(
                            new XYChart.Data<>(1,2),
                            new XYChart.Data<>(5,3),
                            new XYChart.Data<>(9,3),
                            new XYChart.Data<>(2,7),
                            new XYChart.Data<>(6,9),
                            new XYChart.Data<>(1,2)
                    )),
                    new LineChart.Series<>("Series 2",FXCollections.observableArrayList(
                            new XYChart.Data<>(5,7),
                            new XYChart.Data<>(3,4),
                            new XYChart.Data<>(8,2),
                            new XYChart.Data<>(6,9),
                            new XYChart.Data<>(1,3),
                            new XYChart.Data<>(9,7)
                    )));

    lineChart.setData(dataChart);

例如更改第一个列表的第二个点的the "y" axis Serie 1,其当前值为 (x: 5, y: 3) 看起来像这样

dataChart.get(0).getData().get(1).setYValue(9);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-03
    • 1970-01-01
    • 1970-01-01
    • 2012-08-17
    • 2016-12-16
    相关资源
    最近更新 更多