【问题标题】:How to clear text added in a javafx barchart?如何清除在 javafx 条形图中添加的文本?
【发布时间】:2015-12-15 09:59:05
【问题描述】:

我在条的顶部添加了一些文本(每个条的值)。它正在工作,但问题是我想在每次更新图表时删除此文本。实际上,更新数据后文本仍然存在。

对于第一个图表,由于没有以前的数据,它可以正确显示。

但是在我更新图表的数据后,我得到了以下结果(它正在工作,但之前添加的文本仍然存在......):

我用红色标出了错误的部分

我用这种方法在每个条的顶部添加文本:

private void displayLabelForData(XYChart.Data<String, Number> data) {
    final Node node = data.getNode();
    final Text dataText = new Text(data.getYValue() + "");
    node.parentProperty().addListener(new ChangeListener<Parent>() {
      @Override public void changed(ObservableValue<? extends Parent> ov, Parent oldParent, Parent parent) {
        Group parentGroup = (Group) parent;
        parentGroup.getChildren().add(dataText);
      }
    });

    node.boundsInParentProperty().addListener(new ChangeListener<Bounds>() {
      @Override public void changed(ObservableValue<? extends Bounds> ov, Bounds oldBounds, Bounds bounds) {
        dataText.setLayoutX(
          Math.round(
            bounds.getMinX() + bounds.getWidth() / 2 - dataText.prefWidth(-1) / 2
          )
        );
        dataText.setLayoutY(
          Math.round(
            bounds.getMinY() - dataText.prefHeight(-1) * 0.5
          )
        );
      }
    });
}

我的完整代码可用on Gist

仍然没有答案......

谢谢!

【问题讨论】:

  • @jewelsea 我知道它是相关的,我用你的代码来做到这一点:) 但是你没有解释如何更新/清理这些标签,我尝试了几个小时没有成功。 .

标签: java text javafx charts bar-chart


【解决方案1】:

我个人更喜欢继承 BarChart 并覆盖正确的方法。

这只是一个满足您需求的演示,还有待改进,即。 e.覆盖所有相关方法、文本格式化程序、文本居中、调整图表区域大小以适应添加的文本等。但我想你会明白的:

import java.util.HashMap;
import java.util.Map;

import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.chart.Axis;
import javafx.scene.chart.BarChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class BarChartSample extends Application {

    final static String austria = "Austria";
    final static String brazil = "Brazil";
    final static String france = "France";
    final static String italy = "Italy";
    final static String usa = "USA";

    /**
     * Barchart with a clear button
     */
    @Override
    public void start(Stage stage) {

        final CategoryAxis xAxis = new CategoryAxis();
        final NumberAxis yAxis = new NumberAxis();
        final BarChartExt<String, Number> bc = new BarChartExt<String, Number>(xAxis, yAxis);

        bc.setTitle("Country Summary");
        xAxis.setLabel("Country");
        yAxis.setLabel("Value");

        XYChart.Series series1 = new XYChart.Series();
        series1.setName("2003");
        series1.getData().add(new XYChart.Data(austria, 25601.34));
        series1.getData().add(new XYChart.Data(brazil, 20148.82));
        series1.getData().add(new XYChart.Data(france, 10000));
        series1.getData().add(new XYChart.Data(italy, 35407.15));
        series1.getData().add(new XYChart.Data(usa, 12000));

        XYChart.Series series2 = new XYChart.Series();
        series2.setName("2004");
        series2.getData().add(new XYChart.Data(austria, 57401.85));
        series2.getData().add(new XYChart.Data(brazil, 41941.19));
        series2.getData().add(new XYChart.Data(france, 45263.37));
        series2.getData().add(new XYChart.Data(italy, 117320.16));
        series2.getData().add(new XYChart.Data(usa, 14845.27));

        XYChart.Series series3 = new XYChart.Series();
        series3.setName("2005");
        series3.getData().add(new XYChart.Data(austria, 45000.65));
        series3.getData().add(new XYChart.Data(brazil, 44835.76));
        series3.getData().add(new XYChart.Data(france, 18722.18));
        series3.getData().add(new XYChart.Data(italy, 17557.31));
        series3.getData().add(new XYChart.Data(usa, 92633.68));

        bc.getData().addAll(series1, series2, series3);

        Button clearButton = new Button("Clear");
        clearButton.setOnAction(e -> {

            bc.getData().clear();

        });

        HBox toolbar = new HBox();
        toolbar.getChildren().addAll(clearButton);

        VBox root = new VBox();
        VBox.setVgrow(bc, Priority.ALWAYS);
        root.getChildren().addAll(toolbar, bc);

        Scene scene = new Scene(root, 800, 600);

        stage.setScene(scene);
        stage.show();
    }

    /**
     * Custom barchart with text on top of bars
     *
     * @param <X>
     * @param <Y>
     */
    private static class BarChartExt<X, Y> extends BarChart<X, Y> {

        /**
         * Registry for text nodes of the bars
         */
        Map<Node, Node> nodeMap = new HashMap<>();

        public BarChartExt(Axis xAxis, Axis yAxis) {
            super(xAxis, yAxis);
        }

        /**
         * Add text for bars
         */
        @Override
        protected void seriesAdded(Series<X, Y> series, int seriesIndex) {

            super.seriesAdded(series, seriesIndex);

            for (int j = 0; j < series.getData().size(); j++) {

                Data<X, Y> item = series.getData().get(j);

                Node text = new Text(String.valueOf(item.getYValue()));
                nodeMap.put(item.getNode(), text);
                getPlotChildren().add(text);

            }

        }

        /**
         * Remove text of bars
         */
        @Override
        protected void seriesRemoved(final Series<X, Y> series) {

            for (Node bar : nodeMap.keySet()) {

                Node text = nodeMap.get(bar);
                getPlotChildren().remove(text);

            }

            nodeMap.clear();

            super.seriesRemoved(series);
        }

        /**
         * Adjust text of bars, position them on top
         */
        @Override
        protected void layoutPlotChildren() {

            super.layoutPlotChildren();

            for (Node bar : nodeMap.keySet()) {

                Node text = nodeMap.get(bar);

                text.relocate(bar.getBoundsInParent().getMinX(), bar.getBoundsInParent().getMinY() - 30);

            }

        }
    }

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

截图:

【讨论】:

  • 条形顶部没有文字...在seriesAdded(Series&lt;X, Y&gt; series, int seriesIndex), series.getData().size() = 0
  • 什么意思?我添加了一个屏幕截图,我看到了文字。顺便说一句,这只是一个草案,正确实施它需要更多的代码和努力。这是一个建议,我不喜欢事件解决方案,因为正如您所见,它会变得非常复杂。通常你需要更多额外的东西,比如标记等等,所以对条形图类进行子类化对我来说似乎是正确的方法。但这是你的决定。如果不是您要搜索的内容,我可以再次删除答案。
  • 您的解决方案很棒,我正在尝试实施它。但是现在我在条形图上看不到任何值,这肯定是我的错。我正在寻找原因。
  • 如果您只是复制/粘贴我发布的代码并执行它,您应该会看到它们。顺便说一句,我在 Windows 上使用了 Java 8u65。哦,看看my post here (不相关的)也可能会给你一个想法。
  • 我不能只是复制/粘贴,因为我在 Swing 应用程序中。
【解决方案2】:

多亏了 Roland,我能够创建以下具有固定对齐的图表。由于缺乏针对此问题的建议解决方案,这可能对某人有所帮助。

public class CustomBarChart<X, Y> extends BarChart<X, Y> {

    Map<Node, TextFlow> nodeMap = new HashMap<>();

    public CustomBarChart(Axis xAxis, Axis yAxis) {
        super(xAxis, yAxis);
        this.setBarGap(0.0);
    }

    @Override
    protected void seriesAdded(Series<X, Y> series, int seriesIndex) {

        super.seriesAdded(series, seriesIndex);

        for (int j = 0; j < series.getData().size(); j++) {

            Data<X, Y> item = series.getData().get(j);

            Text text = new Text(item.getYValue().toString());
            text.setStyle("-fx-font-size: 10pt;");

            TextFlow textFlow = new TextFlow(text);
            textFlow.setTextAlignment(TextAlignment.CENTER);

            nodeMap.put(item.getNode(), textFlow);
            this.getPlotChildren().add(textFlow);

        }

    }

    @Override
    protected void seriesRemoved(final Series<X, Y> series) {

        for (Node bar : nodeMap.keySet()) {

            Node text = nodeMap.get(bar);
            this.getPlotChildren().remove(text);

        }

        nodeMap.clear();

        super.seriesRemoved(series);
    }

    @Override
    protected void layoutPlotChildren() {

        super.layoutPlotChildren();

        for (Node bar : nodeMap.keySet()) {

            TextFlow textFlow = nodeMap.get(bar);

            if (bar.getBoundsInParent().getHeight() > 30) {
                ((Text) textFlow.getChildren().get(0)).setFill(Color.WHITE);
                textFlow.resize(bar.getBoundsInParent().getWidth(), 200);
                textFlow.relocate(bar.getBoundsInParent().getMinX(), bar.getBoundsInParent().getMinY() + 10);
            } else {
                ((Text) textFlow.getChildren().get(0)).setFill(Color.GRAY);
                textFlow.resize(bar.getBoundsInParent().getWidth(), 200);
                textFlow.relocate(bar.getBoundsInParent().getMinX(), bar.getBoundsInParent().getMinY() - 20);
            }
        }
    }
}

【讨论】:

    【解决方案3】:

    我只找到了一种解决方案,就是在收到更新时清除每个Data的每个parentNode。然后,您将重建整个系列。 如下,它可以工作,但我没有检查性能。在我的环境中它非常快:

    ObservableList<Series<String, Number>> allSeries = yourBarChart.getData();
        if (updateReceived) {
            for (XYChart.Series<String, Number> series : allSeries) {
                for (XYChart.Data<String, Number> data : series.getData()) {
                    Node node = data.getNode();
                    Parent parent = node.parentProperty().get();
                    if (parent != null && parent instanceof Group) {
                        Group group = (Group) parent;
                        group.getChildren().clear();
                    }
                }
            }
            allSeries.clear();
        }
    

    【讨论】:

    • 如果有人知道如何定位负 Y 条的文本,我无法弄清楚......即使当我的 Y 值为负时我修改 dataText 的 layoutY
    • 找到了,我只是修改了 boundsInParentProperty 如下: double yBound = 0D; if(data.getYValue().doubleValue()
    猜你喜欢
    • 2017-10-23
    • 1970-01-01
    • 1970-01-01
    • 2021-07-06
    • 1970-01-01
    • 1970-01-01
    • 2014-05-15
    • 2020-09-08
    • 2014-06-04
    相关资源
    最近更新 更多