gpt4 book ai didi

java - 如何强制更新 JavaFX XYChart 的布局

转载 作者:行者123 更新时间:2023-12-01 17:41:40 28 4
gpt4 key购买 nike

我试图在计时器方法中强制更新自定义 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() 方法中更改场景图(更改矩形的尺寸和/或更改图表的子图),该方法(我believe)被称为布局过程的一部分。因此,当您请求布局时,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);
}
}

关于java - 如何强制更新 JavaFX XYChart 的布局,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60328906/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com