【问题标题】:JavaFx TreeTableCell width calculationJavaFx TreeTableCell 宽度计算
【发布时间】:2018-05-25 01:44:54
【问题描述】:

在我的应用程序中,我正在显示 TreeTableView 并希望增加缩进。我找到了相应的 CSS 属性 -fx-indent,它工作得非常好。 但是我现在正在努力计算首选宽度。 我希望该专栏能够完美地包装其内容并使用

中所述的反射来实现这一点

Auto resize column to fit content

这可以使用默认缩进,但它似乎不包括我在计算中的更改。通过挖掘源代码,我在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;
...

调试器显示,代码 sn-p 工作并将增加的缩进添加到填充中,但是单元格的 prefWidth 保持不变并且我的文本超出了...

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

非常感谢您!

【问题讨论】:

    标签: javafx javafx-8 openjdk openjfx


    【解决方案1】:

    编辑

    看起来问题已经用 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);
    }
    

    所以我试图了解这个过程是如何运作的。看来,resizeColumnToFitContent FX 中的 resizeColumnToFitContent 方法会迭代显示的值,并且对于每个值,它都会构造一个新的单元格和一个新的行。 然后它将单元格放入行中,将行添加到当前视图中,然后读取计算的 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 将关联的行设置为TreeTableRow 类型。 结果,instanceofcheck 失败,而不是自定义缩进,魔术默认常量 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);
    }
    

    【讨论】:

    • +1 用于出色的挖掘和大胆的解决方案 :) 顺便说一句,我怀疑 instanceof 检查失败是因为该行没有皮肤(它从未在任何地方添加)。请注意,在 fx9 中,方法 resizeColumnToFitContent 移动到包私有类的静态实用程序方法中,无法访问...
    • 是的,这可能是原因...感谢您对 fx9 的建议。我曾希望该方法可以使用 fx9 转移到公共 API 并且当我在任何地方都找不到它时感到非常惊讶...
    • 我们都是乐观主义者,不是吗 - 即使总是一次又一次地失望 ;)
    • 刚刚检查了 fx9:自定义缩进(通过 -fx-indent 定义)似乎按预期工作,看起来该错误已修复
    • 好吧,总得有人保持希望 :) 感谢您的快速跟进,我编辑了答案,并附上了 fx9 中的错误修复说明
    猜你喜欢
    • 2012-10-12
    • 1970-01-01
    • 2010-12-07
    • 1970-01-01
    • 2018-07-10
    • 1970-01-01
    • 2012-03-26
    • 2011-01-08
    • 2016-12-01
    相关资源
    最近更新 更多