TableCell:如何使用StackedBarChart(或者它是不可能的)?

我的实验的触发器是最近的一个问题 – 连续的一个单元格应该可视化同一行中几个单元格值中的值的相对比例。 在fx中,StackedBarChart支持这种可视化(退化为单个类别,yAxis是类别轴)。

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

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

此外,有一些轻微的视觉怪癖无法找到一种方法来根据需要限制单元格的高度。

问题:

  • 如何使其正常工作,或
  • 怎么了,渲染机制的哪一部分干扰了?

这个例子:

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 = FXCollections.observableArrayList(); for (int i = 0; i<10; i++) data.add(new Data()); TableView tv = new TableView(data); TableColumn col1 = new TableColumn("num1"); TableColumn 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 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 { NumberAxis xAxisHoriz = new NumberAxis(); CategoryAxis yAxisHoriz = new CategoryAxis(); StackedBarChart sbcHoriz = new StackedBarChart(xAxisHoriz, yAxisHoriz); XYChart.Series series1Horiz = new XYChart.Series(); XYChart.Series 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 series1Horiz = new XYChart.Series(); // XYChart.Series 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。 报告为RT-39884

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

  col3.setCellFactory((TableColumn param) -> { return new TableCell() { @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 series1Horiz = new XYChart.Series<>(); XYChart.Series series2Horiz = new XYChart.Series<>(); StackedBarChart 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,我无法使图表更小。

样本图片