JavaFX图表自动缩放错误,数字较低
我正在使用JavaFX构建StackedBarChart。随着新数据的到来,图表将发生变化。我用一个更新图表的按钮来模拟这个过程 它基本上可以正常工作,但我注意到,对于较低的值(值小于~100),当我第一次更新图表时,Y轴标签似乎有点偏离: 更奇怪的是,如果我第二次(或第三次、第四次…)更新图表,Y轴的自动缩放就会偏离: 如果使用较大的值(值>~1000),则自动缩放效果良好。如果禁用图表动画,则自动缩放效果良好。在我第一次更新图表时,自动缩放效果很好,但之后就不行了 这是我正在使用的代码,它与JavaFX教程中的代码几乎相同JavaFX图表自动缩放错误,数字较低,java,javafx,Java,Javafx,我正在使用JavaFX构建StackedBarChart。随着新数据的到来,图表将发生变化。我用一个更新图表的按钮来模拟这个过程 它基本上可以正常工作,但我注意到,对于较低的值(值小于~100),当我第一次更新图表时,Y轴标签似乎有点偏离: 更奇怪的是,如果我第二次(或第三次、第四次…)更新图表,Y轴的自动缩放就会偏离: 如果使用较大的值(值>~1000),则自动缩放效果良好。如果禁用图表动画,则自动缩放效果良好。在我第一次更新图表时,自动缩放效果很好,但之后就不行了 这是我正在使用的代码,
import java.util.Arrays;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StackedBarChartSample extends Application {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final StackedBarChart<String, Number> sbc = new StackedBarChart<String, Number>(xAxis, yAxis);
@Override
public void start(Stage stage) {
stage.setTitle("Bar Chart Sample");
sbc.setAnimated(true); //change this to false and autoscaling works!
Button button = new Button("update");
button.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent arg0) {
updateChart();
}
});
VBox contentPane = new VBox();
contentPane.getChildren().addAll(sbc, button);
Scene scene = new Scene(contentPane, 800, 600);
stage.setScene(scene);
stage.show();
}
private void updateChart(){
int m = 1; //change this to 100 and autoscaling works!
xAxis.setCategories(FXCollections.<String>observableArrayList());
sbc.getData().clear();
final XYChart.Series<String, Number> series1 = new XYChart.Series<String, Number>();
final XYChart.Series<String, Number> series2 = new XYChart.Series<String, Number>();
series1.setName("ABC");
series1.getData().add(new XYChart.Data<String, Number>("one", 25*m));
series1.getData().add(new XYChart.Data<String, Number>("two", 20*m));
series1.getData().add(new XYChart.Data<String, Number>("three", 10*m));
series2.setName("XYZ");
series2.getData().add(new XYChart.Data<String, Number>("one", 25*m));
series2.getData().add(new XYChart.Data<String, Number>("two", 20*m));
series2.getData().add(new XYChart.Data<String, Number>("three", 10*m));
xAxis.setCategories(FXCollections.<String>observableArrayList(Arrays.asList("one", "two", "three")));
sbc.getData().addAll(series1, series2);
}
public static void main(String[] args) {
launch(args);
}
}
导入java.util.array;
导入javafx.application.application;
导入javafx.collections.FXCollections;
导入javafx.event.ActionEvent;
导入javafx.event.EventHandler;
导入javafx.scene.scene;
导入javafx.scene.chart.CategoryAxis;
导入javafx.scene.chart.NumberAxis;
导入javafx.scene.chart.StackedBarChart;
导入javafx.scene.chart.XYChart;
导入javafx.scene.control.Button;
导入javafx.scene.layout.VBox;
导入javafx.stage.stage;
公共类StackedBarChartSample扩展应用程序{
最终CategoryAxis xAxis=新CategoryAxis();
最终数字axis yAxis=新数字axis();
最终堆叠条形图sbc=新堆叠条形图(xAxis,yAxis);
@凌驾
公众假期开始(阶段){
阶段。设置标题(“条形图样本”);
sbc.setAnimated(true);//将其更改为false,自动缩放工作正常!
按钮按钮=新按钮(“更新”);
setOnAction(新的EventHandler(){
@凌驾
公共无效句柄(ActionEvent arg0){
updateChart();
}
});
VBox contentPane=新的VBox();
contentPane.getChildren().addAll(sbc,按钮);
场景=新场景(contentPane,800600);
舞台场景;
stage.show();
}
私有void updateChart(){
int m=1;//将此值更改为100,自动缩放工作正常!
setCategories(FXCollections.observableArrayList());
sbc.getData().clear();
最终XYChart.Series系列1=新的XYChart.Series();
最终的XYChart.Series系列2=新的XYChart.Series();
系列1.集合名(“ABC”);
series1.getData().add(新的XYChart.Data(“一”,25*m));
series1.getData().add(新的XYChart.Data(“两个”,20*m));
series1.getData().add(新的XYChart.Data(“三”,10*m));
序列2.集合名(“XYZ”);
series2.getData().add(新的XYChart.Data(“一”,25*m));
series2.getData().add(新的XYChart.Data(“两个”,20*m));
series2.getData().add(新的XYChart.Data(“三”,10*m));
setCategories(FXCollections.observableArrayList(Arrays.asList(“一”、“二”、“三”)));
sbc.getData().addAll(系列1、系列2);
}
公共静态void main(字符串[]args){
发射(args);
}
}
附加问题:动画即使可以工作,也会显示每个堆叠条的两个额外部分,这些部分在动画完成时消失。有没有办法在动画制作过程中去掉这些多余的部分?问题源于两个方面
sbc.getData().clear()
并添加新数据sbc.getData().addAll(系列1、系列2)代码>同时导致两组动画同时启动
我已经修改了你的代码(在文章的末尾),将更新功能分为清除和更新功能,并为它们添加了各种按钮
- 清楚的
- 更新
- 清除并更新
- 清除并稍后更新
- 清除(无动画)和更新
并排运行程序两次,一次使用小值,一次使用大值
如果点击“更新”,您会看到这两个选项的数据都会显示出来。
如果清除该数据,则会看到“移除”动画,但会注意y轴比例。这两个数据集都是相同的。即使使用数百万的值,它也保持不变
现在,如果您正在执行“清除并更新”,则yAxis将适合设置动画的较大值。
使用较小的值时,旧数据占主导地位,因为它在动画期间会增长并保持比新数据大。
对于较大的值,新数据占主导地位,因为它大于旧数据的最终终点
然后,在“移除”动画结束时,旧数据被清除,但y轴被单独保留,在小值看起来完全模糊的情况下
我个人觉得这是JavaFX的一个bug。它几乎感觉像动画开始和停止与y在650。我之所以这样说,是因为当添加具有小值的数据时,条形图似乎会下降到适当的位置,但对于大值,它们似乎会增长到适当的位置。我不能肯定,因为我没有做过代码潜水
我想到的第一个解决方案是在所有动画完成后,告诉y轴再次自动缩放。然而,我认为目前还没有一种方法可以在图表动画制作完成后得到通知。这可能是错误的,但我认为这并不重要。酒吧规模将非常小的大小,然后再变大。最终的结果是好的,但一个奇怪的和延迟的用户体验
编辑:正如罗兰在下面评论的那样,下一段中的方法有点代码味道。我把它留在这里是因为它可以工作,但不要使用它。要么使用我最后的建议,要么使用罗兰在回答中提供的方式
解决方法是不要同时启动两个动画。最后运行的动画需要是添加数据后的动画。这可以在“稍后清除和更新”按钮的代码中看到。
霍维夫
import java.util.Arrays;
import java.util.Timer;
import java.util.TimerTask;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.StackedBarChart;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class StackedBarChartSample extends Application
{
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final StackedBarChart<String, Number> sbc = new StackedBarChart<String, Number>(xAxis, yAxis);
@Override
public void start(final Stage stage)
{
stage.setTitle("Bar Chart Sample");
sbc.setAnimated(true);
final Button updateButton = new Button("update");
updateButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(final ActionEvent arg0)
{
updateChart();
}
});
final Button clearButton = new Button("Clear");
clearButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(final ActionEvent arg0)
{
clearChart(true);
}
});
final Button clearAndUpdateButton = new Button("Clear & Update");
clearAndUpdateButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(final ActionEvent evt)
{
clearChart(true);
updateChart();
}
});
final Button clearAndUpdateLaterButton = new Button("Clear & Update Later");
clearAndUpdateLaterButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(final ActionEvent evt)
{
clearChart(true);
new Timer(true).schedule(new TimerTask()
{
@Override
public void run()
{
Platform.runLater(() -> updateChart());
}
}, 1000);
}
});
final Button clearNoAnimAndUpdateButton = new Button("Clear (no anim) & Update");
clearNoAnimAndUpdateButton.setOnAction(new EventHandler<ActionEvent>()
{
@Override
public void handle(final ActionEvent evt)
{
clearChart(false);
updateChart();
}
});
final VBox contentPane = new VBox();
contentPane.getChildren().addAll(sbc, clearButton, updateButton, clearAndUpdateButton, clearAndUpdateLaterButton,
clearNoAnimAndUpdateButton);
final Scene scene = new Scene(contentPane, 800, 600);
stage.setScene(scene);
stage.show();
xAxis.setCategories(FXCollections.<String> observableArrayList(Arrays.asList("one", "two", "three")));
}
private void clearChart(final boolean animate)
{
System.out.println("Clear");
final boolean wasAnimated = sbc.getAnimated();
if (!animate) {
sbc.setAnimated(false);
}
sbc.getData().clear();
if (!animate) {
sbc.setAnimated(wasAnimated);
}
}
private void updateChart()
{
System.out.println("Update");
final int m = 1; // change this to 100 and autoscaling works!
final XYChart.Series<String, Number> series1 = new XYChart.Series<String, Number>();
final XYChart.Series<String, Number> series2 = new XYChart.Series<String, Number>();
series1.setName("ABC");
series1.getData().add(new XYChart.Data<String, Number>("one", 25 * m));
series1.getData().add(new XYChart.Data<String, Number>("two", 20 * m));
series1.getData().add(new XYChart.Data<String, Number>("three", 10 * m));
series2.setName("XYZ");
series2.getData().add(new XYChart.Data<String, Number>("one", 25 * m));
series2.getData().add(new XYChart.Data<String, Number>("two", 20 * m));
series2.getData().add(new XYChart.Data<String, Number>("three", 10 * m));
sbc.getData().addAll(series1, series2);
}
public static void main(final String[] args)
{
launch(args);
}
}
public class StackedBarChartSample extends Application {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final StackedBarChart<String, Number> sbc = new StackedBarChart<String, Number>(xAxis, yAxis);
Random rnd = new Random();
@Override
public void start(Stage stage) {
stage.setTitle("Bar Chart Sample");
sbc.setAnimated(true); //change this to false and autoscaling works!
Button button = new Button("update");
button.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent arg0) {
updateChart();
}
});
VBox contentPane = new VBox();
contentPane.getChildren().addAll(sbc, button);
Scene scene = new Scene(contentPane, 800, 600);
stage.setScene(scene);
stage.show();
}
private void updateChart(){
int m = 1; //change this to 100 and autoscaling works!
m = rnd.nextInt(100) + 1; // just some random value to see changes in the chart
System.out.println( "m = " + m);
final XYChart.Series<String, Number> series1 = new XYChart.Series<String, Number>();
final XYChart.Series<String, Number> series2 = new XYChart.Series<String, Number>();
series1.setName("ABC");
series1.getData().add(new XYChart.Data<String, Number>("one", 25*m));
series1.getData().add(new XYChart.Data<String, Number>("two", 20*m));
series1.getData().add(new XYChart.Data<String, Number>("three", 10*m));
series2.setName("XYZ");
series2.getData().add(new XYChart.Data<String, Number>("one", 25*m));
series2.getData().add(new XYChart.Data<String, Number>("two", 20*m));
series2.getData().add(new XYChart.Data<String, Number>("three", 10*m));
sbc.setData( FXCollections.observableArrayList(series1, series2));
}
public static void main(String[] args) {
launch(args);
}
}
public class StackedBarChartSample extends Application {
final CategoryAxis xAxis = new CategoryAxis();
final NumberAxis yAxis = new NumberAxis();
final StackedBarChart<String, Number> sbc = new StackedBarChart<String, Number>(xAxis, yAxis);
Random rnd = new Random();
final XYChart.Series<String, Number> series1 = new XYChart.Series<String, Number>();
final XYChart.Series<String, Number> series2 = new XYChart.Series<String, Number>();
@Override
public void start(Stage stage) {
stage.setTitle("Bar Chart Sample");
sbc.setAnimated(true); //change this to false and autoscaling works!
Button createButton = new Button("Create");
createButton.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent arg0) {
createChartData();
}
});
Button modifyButton = new Button("Modify");
modifyButton.setOnAction(new EventHandler<ActionEvent>(){
@Override
public void handle(ActionEvent arg0) {
modifyChartData();
}
});
VBox contentPane = new VBox();
contentPane.getChildren().addAll(sbc, createButton, modifyButton);
Scene scene = new Scene(contentPane, 800, 600);
stage.setScene(scene);
stage.show();
}
private void createChartData(){
int m = 1; //change this to 100 and autoscaling works!
m = rnd.nextInt(100) + 1; // just some random value to see changes in the chart
System.out.println( "m = " + m);
series1.setName("ABC");
series1.getData().add(new XYChart.Data<String, Number>("one", 25*m));
series1.getData().add(new XYChart.Data<String, Number>("two", 20*m));
series1.getData().add(new XYChart.Data<String, Number>("three", 10*m));
series2.setName("XYZ");
series2.getData().add(new XYChart.Data<String, Number>("one", 25*m));
series2.getData().add(new XYChart.Data<String, Number>("two", 20*m));
series2.getData().add(new XYChart.Data<String, Number>("three", 10*m));
sbc.setData( FXCollections.observableArrayList(series1, series2));
}
private void modifyChartData() {
int m = 1; //change this to 100 and autoscaling works!
m = rnd.nextInt(100) + 1; // just some random value to see changes in the chart
System.out.println( "m = " + m);
for( XYChart.Data<String,Number> data: series1.getData()) {
data.setYValue(rnd.nextInt(30) * m);
}
for( XYChart.Data<String,Number> data: series2.getData()) {
data.setYValue(rnd.nextInt(30) * m);
}
}
public static void main(String[] args) {
launch(args);
}
}