【问题标题】:Adding values on top of each on a realtime javafx bar chart [duplicate]在实时javafx条形图的每个顶部添加值[重复]
【发布时间】:2020-09-02 01:22:35
【问题描述】:

我是 Java 新手,我正在尝试创建一个实时 javaFX 条形图,它显示每个条形顶部的更新值。 我得到了这个每秒更新的条形图。如何使用此图表在每个条形顶部添加值。网上还有其他例子,但与此代码上的实时条形图不兼容。我怎样才能在每个栏中添加文本。谢谢

import java.util.Calendar;
import java.util.TimeZone;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.stage.Stage;


public class App extends Application {
    private ScheduledExecutorService scheduledExecutorService;

    final static String austria = "Austria";
    final static String brazil = "Brazil";
    final static String france = "France";

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Realtime Bar Chart Demo");

        //defining the axes
        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();

        xAxis.setAnimated(false);
        yAxis.setAnimated(false);

        //creating the bar chart with two axis
        final BarChart<String,Number> bc =  new BarChart<>(xAxis,yAxis);
        bc.setAnimated(false);
        bc.setTitle("Country Summary");
        xAxis.setLabel("Country");
        yAxis.setLabel("Value");

        //defining a series to display data
        XYChart.Series<String, Number> seriesA = new XYChart.Series<>();
        seriesA.setName("Austra");

        XYChart.Series<String, Number> seriesB = new XYChart.Series<>();
        seriesB.setName("Brazil");

        XYChart.Series<String, Number> seriesC = new XYChart.Series<>();
        seriesC.setName("France");

        // add series to chart
        bc.getData().add(seriesA);
        bc.getData().add(seriesB);
        bc.getData().add(seriesC);

        // setup scene
        Scene scene = new Scene(bc, 800, 600);
        primaryStage.setScene(scene);

        // show the stage
        primaryStage.show();

        // setup a scheduled executor to periodically put data into the chart
        scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

        // input data onto graph per second
        scheduledExecutorService.scheduleAtFixedRate(() -> {

            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
            int secondA = cal.get(Calendar.SECOND);
            int secondB = secondA + 27;
            int secondC = secondA + 14;

            // Update the chart
            Platform.runLater(() -> {
                // input realtime data number
                seriesA.getData().add(new XYChart.Data<>(austria, secondA));
                seriesB.getData().add(new XYChart.Data<>(brazil, secondB));
                seriesC.getData().add(new XYChart.Data<>(france, secondC));
            });
        }, 0, 1, TimeUnit.SECONDS);
    }

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

【问题讨论】:

  • 请发帖minimal reproducible example。发布的代码无法编译。
  • 我更新了代码,现在应该可以编译了。
  • 你测试了吗? Austria 是否编译? Calendar.SECONDS 编译吗?您要求这里的人们自愿解决您的问题,您应该让他们尽可能轻松地解决问题。
  • 在我的电脑上它正在编译和更新数据。我复制了一份到我的手机上,因为我有一段时间不在家。我是否还必须包括进口?
  • hmm .. 没有看到 在每个栏的顶部显示更新值的任何代码 - 静态执行(或单击按钮),然后继续执行它来自后台线程。

标签: java javafx charts


【解决方案1】:

以下是在您的代码中实现一些更改并使用this answer 中提出的解决方案的mre:

import java.util.Calendar;
import java.util.TimeZone;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Bounds;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.chart.XYChart.Data;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class App extends Application {
    private ScheduledExecutorService scheduledExecutorService;

    final static String austria = "Austria",  brazil = "Brazil",  france = "France";
    private IntegerProperty secondA,  secondB , secondC; 
    private Text secondAText, secondBText , secondCText;

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        primaryStage.setTitle("Realtime Bar Chart Demo");

        //defining the axes
        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();

        xAxis.setAnimated(false);
        yAxis.setAnimated(false);

        //creating the bar chart with two axis
        final BarChart<String,Number> bc =  new BarChart<>(xAxis,yAxis);
        bc.setAnimated(false);
        bc.setTitle("Country Summary");
        xAxis.setLabel("Country");
        yAxis.setLabel("Value");

        //defining a series to display data
        XYChart.Series<String, Number> seriesA = new XYChart.Series<>();
        Data<String, Number> dataA = new XYChart.Data<>(austria,0);
        seriesA.getData().add(dataA);
        seriesA.setName("Austra");

        secondA = new SimpleIntegerProperty(0);
        secondAText = new Text("");
        secondA.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
            dataA.setYValue(newValue);
            secondAText.setText(String.valueOf(newValue));
        });

        XYChart.Series<String, Number> seriesB = new XYChart.Series<>();
        Data<String, Number> dataB = new XYChart.Data<>(brazil,0);
        seriesB.getData().add(dataB);
        seriesB.setName("Brazil");
        secondB =  new SimpleIntegerProperty(0);
        secondB.bind(secondA.add(27));
        secondBText = new Text("");
        secondB.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
            dataB.setYValue(newValue);
            secondBText.setText(String.valueOf(newValue));
        });

        XYChart.Series<String, Number> seriesC = new XYChart.Series<>();
        Data<String, Number> dataC = new XYChart.Data<>(france,0);
        seriesC.getData().add(dataC);
        seriesC.setName("France");

        secondC =  new SimpleIntegerProperty(0);
        secondC.bind(secondA.add(14));
        secondCText = new Text("");
        secondC.addListener((ChangeListener<Number>) (observable, oldValue, newValue) -> {
            dataC.setYValue(newValue);
            secondCText.setText(String.valueOf(newValue));
        });

        // add series to chart
        bc.getData().add(seriesA);
        bc.getData().add(seriesB);
        bc.getData().add(seriesC);

        displayLabelForData(dataA, secondAText);
        displayLabelForData(dataB, secondBText);
        displayLabelForData(dataC, secondCText);

        // setup scene
        Scene scene = new Scene(bc, 800, 600);
        primaryStage.setScene(scene);

        // show the stage
        primaryStage.show();

        // setup a scheduled executor to periodically put data into the chart
        scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

        // input data onto graph per second
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

            // Update the chart
            Platform.runLater(() -> {
                secondA.set( cal.get(Calendar.SECOND));
            });
        }, 0, 1, TimeUnit.SECONDS);
    }

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

    private void displayLabelForData(XYChart.Data<String, Number> data, Text text) {

        final Node node = data.getNode();
        ((Group) node.getParent()).getChildren().add(text);

        node.boundsInParentProperty().addListener((ChangeListener<Bounds>) (ov, oldBounds, bounds) -> {
            text.setLayoutX(
                    Math.round( bounds.getMinX() + bounds.getWidth() / 2 - text.prefWidth(-1) / 2));
            text.setLayoutY(Math.round( bounds.getMinY() - text.prefHeight(-1) * 0.5));
        });
    }
}

旁注:您可以使用 javafx 工具来运行周期性任务:

    Timeline timeline = new Timeline(new KeyFrame(
            Duration.seconds(1),
            e -> {
                Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
                secondA.set( cal.get(Calendar.SECOND));
            }
     ));
    timeline.setCycleCount(Animation.INDEFINITE);
    timeline.play();

【讨论】:

  • 我正在尝试使用此方法将条形图水平翻转 xAxis.setTickLabelRotation(90);但它不起作用。那么有什么问题呢?
  • a.doesn't work. 不是一个好的问题描述 b.对于横向需要不同的布局计算 c.如果您在水平发布新问题方面需要帮助。
  • @kleopatra 我不知道这是否是家庭作业。我觉得不重要。反正我知道的不多,但我学到的大部分都是从例子中学到的。当我开始学习一门新学科时,我需要与我的用例非常接近的示例。("网上还有其他示例,但与此上的实时条形图不兼容代码",OP)。我尝试以我希望获得帮助的方式帮助他人。
  • 我也认为重复回答的问题不一定是坏事。当然,我不是第一个表达这种观点的人"一些重复是可取的。在一个问题周围有多个微妙的变体通常是有好处的,因为人们倾向于完全使用不同的词,我们的覆盖范围越大,人们找到他们正在寻找的答案的几率就越大" Jeff Atwood
  • 好吧,这里提问者没有尝试 anything 来接近她/他的目标(在问题代码中,没有任何痕迹 其他在线示例,但与实时条形图不兼容)。鉴于她/他对这个答案的评论,看起来很像什么都没学到:) 很高兴错了,不过,我们将在后续问题中看到
猜你喜欢
  • 2019-07-03
  • 1970-01-01
  • 2023-03-04
  • 1970-01-01
  • 2017-07-29
  • 2018-12-02
  • 2022-01-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多