JavaFX图表自动缩放错误,数字较低

JavaFX图表自动缩放错误,数字较低,java,javafx,Java,Javafx,我正在使用JavaFX构建StackedBarChart。随着新数据的到来,图表将发生变化。我用一个更新图表的按钮来模拟这个过程 它基本上可以正常工作,但我注意到,对于较低的值(值小于~100),当我第一次更新图表时,Y轴标签似乎有点偏离: 更奇怪的是,如果我第二次(或第三次、第四次…)更新图表,Y轴的自动缩放就会偏离: 如果使用较大的值(值>~1000),则自动缩放效果良好。如果禁用图表动画,则自动缩放效果良好。在我第一次更新图表时,自动缩放效果很好,但之后就不行了 这是我正在使用的代码,

我正在使用JavaFX构建StackedBarChart。随着新数据的到来,图表将发生变化。我用一个更新图表的按钮来模拟这个过程

它基本上可以正常工作,但我注意到,对于较低的值(值小于~100),当我第一次更新图表时,Y轴标签似乎有点偏离:

更奇怪的是,如果我第二次(或第三次、第四次…)更新图表,Y轴的自动缩放就会偏离:

如果使用较大的值(值>~1000),则自动缩放效果良好。如果禁用图表动画,则自动缩放效果良好。在我第一次更新图表时,自动缩放效果很好,但之后就不行了

这是我正在使用的代码,它与JavaFX教程中的代码几乎相同

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);
}
}

附加问题:动画即使可以工作,也会显示每个堆叠条的两个额外部分,这些部分在动画完成时消失。有没有办法在动画制作过程中去掉这些多余的部分?

问题源于两个方面

  • 同时添加和删除数据(在任何动画开始之前)
  • y轴自动缩放以适应动画数据
  • 通过删除旧数据
    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);
        }
    }