【问题标题】:TableCell: how to use a StackedBarChart (or is it impossible)?TableCell:如何使用 StackedBarChart(或者不可能)?
【发布时间】:2015-01-26 14:39:02
【问题描述】:

我的实验的触发器是 recent question - 一行中的一个单元格应该可视化同一行中多个单元格值中值的相对比例。在 fx 中,StackedBarChart 支持这种可视化(退化为单个类别,yAxis 为类别轴)。

不幸的是,使用单元格图形这样的图表在更新项目时会产生奇怪的效果,具体取决于我们如何进行更新:

  • 场景A:用序列初始化图表,更新序列中的数据。条形仅在第一次显示时显示良好,来回滚动会在内部留下随机“间隙”
  • 场景 B:在每一轮中创建和设置新系列。这些条似乎具有正确的宽度,但它们的颜色在滚动时会随机变化

此外,f.i. 还有一些小的视觉怪癖。找不到根据需要限制单元格高度的方法。

问题:

  • 如何使其正常工作,或
  • 出了什么问题,渲染机制的哪一部分受到干扰?

例子:

import javafx.application.Application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
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.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

/**
 * from SO, how to show relative bars with colors of 
 * a related chart
 * 
 * https://stackoverflow.com/a/28141421/203657
 * 
 * That's a solution with manually calculating and
 * filling a rectangle with base chart colors
 * 
 * Here trying to use StackedBarChart .. problems as noted in cell doc.
 * Extracted TableStackedBarChart for SO question.
 */
public class TableStackedBar extends Application {
    public static void main(String[] args) { launch(args); }

    @Override
    public void start(Stage stage) {
        ObservableList<Data> data = FXCollections.observableArrayList();
        for (int i = 0; i<10; i++) data.add(new Data());

        TableView<Data> tv = new TableView<>(data);
        TableColumn<Data, Number> col1 = new TableColumn<>("num1");
        TableColumn<Data, Number> col2 = new TableColumn<>("num2");
        col1.setCellValueFactory((p)->{return p.getValue().num1;});
        col2.setCellValueFactory((p)->{return p.getValue().num2;});

        //make this column hold the entire Data object so we can access all fields
        TableColumn<Data, Data> col3 = new TableColumn<>("bar");
        col3.setPrefWidth(500);
        col3.setCellValueFactory((p)->{return new ReadOnlyObjectWrapper<>(p.getValue());});

        col3.setCellFactory(p -> new StackedBarChartCell(2000.));
        tv.getColumns().addAll(col1,col2,col3);
        tv.setFixedCellSize(50.);

        Scene scene = new Scene(tv);

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

    /**
     * TableCell that uses a StackedBarChart to visualize relation of 
     * data.
     * 
     * Problems with updating items:
     * - scenario A: updating the series leaves empty patches horizontally
     * - scenario B: re-setting the series changes colors randomly
     * 
     * Other problems
     * - runs amok without fixedCellSize on tableView
     * - can't max the height of the chart (so it's cut-off in the middle
     */
    public static class StackedBarChartCell extends TableCell<Data, Data> {

        NumberAxis xAxisHoriz = new NumberAxis();
        CategoryAxis yAxisHoriz = new CategoryAxis();
        StackedBarChart<Number, String> sbcHoriz = new StackedBarChart<>(xAxisHoriz, yAxisHoriz);
        XYChart.Series<Number, String> series1Horiz = new XYChart.Series<>();
        XYChart.Series<Number, String> series2Horiz = new XYChart.Series<>();


        public StackedBarChartCell(double upperBound) {
            yAxisHoriz.setTickLabelsVisible(false);
            yAxisHoriz.setTickMarkVisible(false);
            yAxisHoriz.setStyle("-fx-border-color: transparent transparent transparent transparent;");

            xAxisHoriz.setTickLabelsVisible(false);
            xAxisHoriz.setTickMarkVisible(false);
            xAxisHoriz.setMinorTickVisible(false);
            xAxisHoriz.setStyle("-fx-border-color: transparent transparent transparent transparent;");
            xAxisHoriz.setAutoRanging(false);
            xAxisHoriz.setUpperBound(upperBound);
            xAxisHoriz.setLowerBound(0.);

            sbcHoriz.setHorizontalGridLinesVisible(false);
            sbcHoriz.setVerticalGridLinesVisible(false);
            sbcHoriz.setLegendVisible(false);
            sbcHoriz.setAnimated(false);

            // scenario A: set series initially
            sbcHoriz.getData().setAll(series1Horiz, series2Horiz);
            sbcHoriz.setCategoryGap(0);
            // no effect
            sbcHoriz.setMaxHeight(20);
        }
        @Override
        protected void updateItem(Data item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setGraphic(null);
            } else {
                setGraphic(sbcHoriz);
                // scenario B: set new series
                // uncomment for scenario A
//                XYChart.Series<Number, String> series1Horiz = new XYChart.Series<>();
//                XYChart.Series<Number, String> series2Horiz = new XYChart.Series<>();
//                sbcHoriz.getData().setAll(series1Horiz, series2Horiz);
                //---- end of scenario B
                series1Horiz.getData().setAll(new XYChart.Data(item.num1.get(), "none"));
                series2Horiz.getData().setAll(new XYChart.Data(item.num2.get(), "none"));
            }
        }

    }


    private static class Data{
        private SimpleIntegerProperty num1 = new SimpleIntegerProperty((int)(Math.random()*1000));
        private SimpleIntegerProperty num2 = new SimpleIntegerProperty((int)(Math.random()*1000));

        public SimpleIntegerProperty num1Property(){return num1;}
        public SimpleIntegerProperty num2Property(){return num2;}
    }
}

更新:似乎是 8u40 的回归 - 适用于 8u20/25,不适用于 8u40b20。 Reported as RT-39884

【问题讨论】:

  • 我对这些内部类的了解总是令人困惑,但也许静态类在彼此之间共享它的变量。如果我在 updateItem 中制作所有内容,那么它可以正常工作。它看起来仍然很可怕。您必须单步执行图表代码才能查看所有尺寸的设置位置。它是轴和图表的某种组合。图表也有很大的默认填充。
  • @brian 感谢您的意见!虽然......静态或内部不应该有所作为,但似乎是 8u40 中的错误/回归,所以任何事情都可能发生 ;-) 在 8u20 中一切正常 - 你的版本/操作系统是什么?
  • jvm 是 Oracle 的 1.8.0_25-b18 但我可能使用的是较早的 jdk,因为我使用的是 XP,我必须手动安装它。
  • @brian 感谢您提供的信息 - 加强了我的回归假设 :)

标签: java javafx-8 tablecell


【解决方案1】:

这就是我将东西复制到 CellFactory 中的地方

    col3.setCellFactory((TableColumn<Data, Data> param) -> {
        return new TableCell<Data, Data>() {

            @Override
            protected void updateItem(Data item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) setGraphic(null);
                else {
                    super.updateItem(item, empty);
                    if (item == null || empty) {
                        setGraphic(null);
                    } else {
                        NumberAxis xAxisHoriz = new NumberAxis(0, 2000, 1000);
                        CategoryAxis yAxisHoriz = new CategoryAxis(FXCollections.observableArrayList(""));
                        XYChart.Series<Number, String> series1Horiz = new XYChart.Series<>();
                        XYChart.Series<Number, String> series2Horiz = new XYChart.Series<>();
                        StackedBarChart<Number, String> sbcHoriz = new StackedBarChart<>(xAxisHoriz, yAxisHoriz);
                        sbcHoriz.getData().setAll(series1Horiz, series2Horiz);

                        yAxisHoriz.setStyle("-fx-border-color: transparent transparent transparent transparent;"
                                + "-fx-tick-labels-visible: false;"
                                + "-fx-tick-mark-visible: false;"
                                + "-fx-minor-tick-visible: false;"
                                + "-fx-padding: 0 0 0 0;");

                        xAxisHoriz.setStyle("-fx-border-color: transparent transparent transparent transparent;"
                                + "-fx-tick-labels-visible: false;"
                                + "-fx-tick-mark-visible: false;"
                                + "-fx-minor-tick-visible: false;"
                                + "-fx-padding: 0 0 0 0;");

                        sbcHoriz.setHorizontalGridLinesVisible(false);
                        sbcHoriz.setVerticalGridLinesVisible(false);
                        sbcHoriz.setLegendVisible(false);
                        sbcHoriz.setAnimated(false);

                        xAxisHoriz.setMaxWidth(100);
                        sbcHoriz.setMaxWidth(100);
                        sbcHoriz.setPadding(Insets.EMPTY);

                        sbcHoriz.setCategoryGap(0);
                        setGraphic(sbcHoriz);
                        series1Horiz.getData().setAll(new XYChart.Data(item.num1.get(), ""));
                        series2Horiz.getData().setAll(new XYChart.Data(item.num2.get(), ""));
                    }
                }
            }
        };
    });

在我设置了这个tv.setFixedCellSize(30);

我还必须将列宽更改为 200,我无法将图表变小。

【讨论】:

  • +1 已验证:每次在 updateItem 中重新创建图表确实在 8u40b20 中工作(虽然很讨厌,但不应该需要) - 可能有助于追踪回归的原因。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-15
  • 2010-10-17
  • 2022-11-03
  • 1970-01-01
  • 2019-09-26
相关资源
最近更新 更多