簡體   English   中英

如何強制更新 JavaFX XYChart 的布局

[英]How to force layout update of a JavaFX XYChart

我試圖在計時器方法中強制更新自定義 XYChart,但似乎唯一有效的是調整窗口大小。

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;

import java.util.*;
import java.util.stream.Collectors;

public class TestCustomLayoutUpdate extends Application {

    private LineChart<Number, Number> chart;
    private NumberAxis xAxis;
    private NumberAxis yAxis;

    private ShopItem currentShopItem;

    class ShopItem {
        private double price;

        public ShopItem(double price) {
            this.price = price;
        }
    }

    @Override
    public void start(Stage primaryStage) {

        createChart();

        Scene scene = new Scene(chart, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.setHeight(600);
        primaryStage.setWidth(400);
        primaryStage.show();

        Random rng = new Random();

        // Note since this is a regular timer not javafx timer that we should use platform run later.
        TimerTask repeatedTask = new TimerTask() {
            public void run() {
                currentShopItem = new ShopItem(rng.nextDouble() * 100);
                Platform.runLater(() -> {
                    chart.layout();
                    chart.requestLayout();
                    xAxis.layout();
                });
            }
        };
        Timer timer = new Timer("Timer");

        long delay  = 1000L;
        long period = 1000L;
        timer.scheduleAtFixedRate(repeatedTask, delay, period);
    }

    public void createChart() {
        xAxis = new NumberAxis();
        yAxis = new NumberAxis();
        xAxis.setAutoRanging(false);
        xAxis.setUpperBound(100);

        chart = new LineChart<Number, Number>(xAxis, yAxis) {
            private List<Shape> shapes = new ArrayList<>();

            @Override
            public void layoutPlotChildren() {
                super.layoutPlotChildren();

                getPlotChildren().removeAll(shapes);
                shapes.clear();

                if (currentShopItem != null) {
                    Rectangle rect = new Rectangle(0, 0, 10, currentShopItem.price);

                    rect.getTransforms().setAll(chartDisplayTransform(xAxis, yAxis));
                    rect.setFill(Color.RED);
                    shapes.add(rect);
                    getPlotChildren().addAll(shapes);
                }
            }
        };
    }

    private Transform chartDisplayTransform(NumberAxis xAxis, NumberAxis yAxis) {
        return new Affine(xAxis.getScale(), 0, xAxis.getDisplayPosition(0), 0, yAxis.getScale(),
                yAxis.getDisplayPosition(0));
    }

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

如果作為圖形一部分的任何節點的屬性發生更改,JavaFX 將自動布局和重繪場景圖形的任何部分。 您構建代碼的方式的問題在於您只在layoutPlotChildren()方法中更改場景圖(更改矩形的尺寸和/或更改圖表的繪圖子項),該方法(我相信)被稱為作為布局過程的一部分。 因此,當您請求布局時,JavaFX 會檢查場景圖中是否有任何內容發生了變化,發現它沒有發生變化,因此不會執行布局。 因此layoutPlotChildren()沒有被調用,所以場景圖沒有改變......

因此,要解決此問題,您只需要確保在基礎數據更改時更新現有矩形,或者更改繪圖子項列表。 您可以通過使用 JavaFX 屬性並從您的圖表子類中觀察它們來完成此操作。 (我想還有其他方法,例如在圖表子類中定義一個更新矩形的方法,並從動畫循環中調用它。但觀察 JavaFX 屬性是 API 首選的方法。)

順便說一句,如果您想定期更改更新圖形的任何內容,在 JavaFX 中執行此操作的首選方法是使用Timeline ,它完全在 JavaFX 線程上運行並且無需考慮變量的同步等。

這是具有這些更改的示例版本,可按需要工作:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.scene.Scene;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import javafx.scene.transform.Affine;
import javafx.scene.transform.Transform;
import javafx.stage.Stage;
import javafx.util.Duration;

public class TestCustomLayoutUpdate extends Application {

    private LineChart<Number, Number> chart;
    private NumberAxis xAxis;
    private NumberAxis yAxis;


    private ObjectProperty<ShopItem> currentShopItem;

    class ShopItem {
        private double price;

        public ShopItem(double price) {
            this.price = price;
        }
    }

    @Override
    public void start(Stage primaryStage) {

        currentShopItem = new SimpleObjectProperty<>();


        createChart();

        Scene scene = new Scene(chart, 600, 400);
        primaryStage.setScene(scene);
        primaryStage.setHeight(600);
        primaryStage.setWidth(400);
        primaryStage.show();

        Random rng = new Random();

        Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(1),  
                evt -> currentShopItem.set(new ShopItem(rng.nextDouble() * 100))
        ));
        timeline.setCycleCount(Animation.INDEFINITE);
        timeline.play();
    }

    public void createChart() {
        xAxis = new NumberAxis();
        yAxis = new NumberAxis();
        xAxis.setAutoRanging(false);
        xAxis.setUpperBound(100);

        chart = new LineChart<Number, Number>(xAxis, yAxis) {

            private List<Shape> shapes = new ArrayList<>();

            private Rectangle rect ;

            // anonymous class constructor:
            {
                rect = new Rectangle(0,0, Color.RED);

                currentShopItem.addListener((obs, oldItem, newItem) -> {
                    if (newItem == null) {
                        rect.setWidth(0);
                        rect.setHeight(0);
                    } else {
                        rect.setWidth(10);
                        rect.setHeight(newItem.price);
                    }

                });
            }

            @Override
            public void layoutPlotChildren() {
                super.layoutPlotChildren();

                getPlotChildren().removeAll(shapes);
                shapes.clear();

                if (currentShopItem != null) {
                    rect.getTransforms().setAll(chartDisplayTransform(xAxis, yAxis));
                    shapes.add(rect);
                    getPlotChildren().addAll(shapes);
                }
            }
        };
    }

    private Transform chartDisplayTransform(NumberAxis xAxis, NumberAxis yAxis) {
        return new Affine(xAxis.getScale(), 0, xAxis.getDisplayPosition(0), 0, yAxis.getScale(),
                yAxis.getDisplayPosition(0));
    }

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM