4

在我的应用程序中,我正在显示 TreeTableView 并希望增加缩进。我发现各自的 CSS 属性-fx-indent工作得很好。但是我现在正在努力计算首选宽度。我希望专栏能够完美地包装其内容并使用反射来实现这一点,如

自动调整列大小以适应内容

这使用默认缩进有效,但它似乎不包括我在计算中的更改。挖掘源代码,我在TreeTableCellSkin课堂上发现了以下片段:

if (isDeferToParentForPrefWidth) {
        // RT-27167: we must take into account the disclosure node and the
        // indentation (which is not taken into account by the LabeledSkinBase.
        return super.computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);
    }
    return columnWidthProperty().get();

编辑:我部分地回答了我之前的问题,发现这个评论是错误的,因为代码被移动到leftLabelPadding()同一个类中的方法:

// RT-27167: we must take into account the disclosure node and the
    // indentation (which is not taken into account by the LabeledSkinBase.
...
double indentPerLevel = 10;
if (treeTableRow.getSkin() instanceof TreeTableRowSkin) {
    indentPerLevel = ((TreeTableRowSkin<?>)treeTableRow.getSkin()).getIndentationPerLevel();
}
leftPadding += nodeLevel * indentPerLevel;
...

调试器显示,代码片段有效并将增加的缩进添加到填充中,但是单元格的 prefWidth 保持不变并且我的文本被溢出......

您对在何处或如何解决此问题有任何想法。任何方向的任何提示都表示赞赏。

非常感谢您!

4

1 回答 1

2

编辑

看起来问题已经用 fx9 解决了,所以下面的解决方案只需要(并且可能)使用 fx8

/编辑

好吧,我做了更多的挖掘,找到了一个(希望是正确的)解释和一个肮脏的解决方案。对解释感兴趣的人可以在这篇文章中进一步阅读,因为这里的所有其他人都是我肮脏的快速修复。

肮脏但有效的解决方案:

  Method fitColumn = TreeTableViewSkin.class.getDeclaredMethod("resizeColumnToFitContent", TreeTableColumn.class, int.class);
  fitColumn.setAccessible(true);
  List<TreeTableColumn<S, ?>> tableColumns = aTreeTableView.getColumns();
  for (TreeTableColumn<S, ?> treeTableColumn : aTreeTableView.getColumns()) {
    fitColumn.invoke(aTreeTableView.getSkin(), treeTableColumn, -1);

    /*
     * FX does not include the altered indentation in the calculation for the prefWidth.
     * Instead it uses a fixed constant of 10 (TreeTableCellSkin.leftLabelPadding()).
     * To ensure the column can still display all its contents the remaining indentation must be added manually.
     * To achieve this the maximum depth of the displayed tree is calculated and multiplied with the remaining indentation per level.
     */
    TreeItem<S> rootItem = aTreeTableView.getRoot();
    Stack<TreeItem<S>> items = new Stack<>();
    if (aTreeTableView.isShowRoot() && rootItem != null) {
      items.push(rootItem);
    } else if (rootItem != null) {
      rootItem.getChildren().forEach(items::push);
    }

    int maxLevel = -1;
    while (!items.isEmpty()) {
      TreeItem<S> curItem = items.pop();
      int curLevel = aTreeTableView.getTreeItemLevel(curItem);
      maxLevel = Math.max(curLevel, maxLevel);
      if (curItem.isExpanded()) {
        curItem.getChildren().forEach(items::push);
      }
    }
    if (maxLevel >= 0) {
      double indentation = 20; // the indentation used in the css stylesheet
      double additionalIndentPerLevel = indentation - 10; // constant from TreeTableCellSkin
      treeTableColumn.setPrefWidth(treeTableColumn.getWidth() + maxLevel * additionalIndentPerLevel);
    }

尝试解释:

如前所述,我正在使用反射来使列宽适合其内容:

Method fitColumn = TreeTableViewSkin.class.getDeclaredMethod("resizeColumnToFitContent", TreeTableColumn.class, int.class);
fitColumn.setAccessible(true);
List<TreeTableColumn<S, ?>> tableColumns = aTreeTableView.getColumns();
for (TreeTableColumn<S, ?> treeTableColumn : aTreeTableView.getColumns()) {
    fitColumn.invoke(aTreeTableView.getSkin(), treeTableColumn, -1);
}

所以我试图了解这个过程是如何工作的。看起来, FXresizeColumnToFitContent中的方法TreeTableViewSkin迭代了显示的值,并为每个值构造了一个新的 Cell 和一个新的 Row。然后它将单元格放入行中,将行添加到当前视图中,然后读取计算的 prefWidth。之后它会删除新的行和单元格。

TreeTableCell<S,?> cell = (TreeTableCell) cellFactory.call(col);
...
TreeTableRow<S> treeTableRow = new TreeTableRow<>();
treeTableRow.updateTreeTableView(treeTableView);
...
getChildren().add(cell);
cell.applyCss();

double w = cell.prefWidth(-1);

maxWidth = Math.max(maxWidth, w);
getChildren().remove(cell);
...

同时TreeTableCellSkin该类使用TableRowSkin与单元格关联的来确定缩进:

// RT-27167: we must take into account the disclosure node and the
    // indentation (which is not taken into account by the LabeledSkinBase.
...
double indentPerLevel = 10;
if (treeTableRow.getSkin() instanceof TreeTableRowSkin) {
    indentPerLevel = ((TreeTableRowSkin<?>)treeTableRow.getSkin()).getIndentationPerLevel();
}
leftPadding += nodeLevel * indentPerLevel;
...

到目前为止,这两个功能都可以正常工作并正确计算宽度。但是,我认为它们不能组合使用。TreeTableCellSkin检查与单元格关联的行是否是所有显示行的实例TreeTableRowSkin。但是,TreeTableViewSkin将关联的行设置为 type TreeTableRow。结果instanceof检查失败,而不是自定义缩进,魔术默认常量10用于宽度计算。

从我的角度来看,我会说这是 JavaFX 中的一个错误,但也许我的结论是错误的。因此,欢迎任何不同的意见或观点。

因此,每列的 prefWidth 将始终使用10每个级别的缩进。因此,如果您更改了缩进,您必须在 FX 计算后手动添加它。为了实现这一点,我计算了显示树的最大深度(我的实现可能不是最有效的,但经过这么多小时我真的不在乎......)。此外,我计算每个级别的缺失缩进。将这两个值相乘,您可以计算出缺失的缩进,只需将其添加到列中:

TreeItem<S> rootItem = aTreeTableView.getRoot();
Stack<TreeItem<S>> items = new Stack<>();
if (aTreeTableView.isShowRoot() && rootItem != null) {
  items.push(rootItem);
} else if (rootItem != null) {
  rootItem.getChildren().forEach(items::push);
}

int maxLevel = -1;
while (!items.isEmpty()) {
  TreeItem<S> curItem = items.pop();
  int curLevel = aTreeTableView.getTreeItemLevel(curItem);
  maxLevel = Math.max(curLevel, maxLevel);
  if (curItem.isExpanded()) {
    curItem.getChildren().forEach(items::push);
  }
}
if (maxLevel >= 0) {
  double indentation = 20; // the indentation used in the css stylesheet
  double additionalIndentPerLevel = indentation - 10; // constant from TreeTableCellSkin
  treeTableColumn.setPrefWidth(treeTableColumn.getWidth() + maxLevel * additionalIndentPerLevel);
}
于 2017-12-12T09:12:52.093 回答