JavaFx TreeTableCell 宽度计算
JavaFx TreeTableCell width calculation
在我的应用程序中,我正在显示一个 TreeTableView 并希望增加缩进。我发现相应的 CSS 属性 -fx-indent
工作得很好。
但是,我现在正在努力计算首选宽度。
我希望该专栏能够完美地包装其内容,并使用
中所述的反射来实现这一点
Auto resize column to fit content
这可以使用默认缩进,但它似乎不包括我在计算中所做的更改。通过源代码挖掘,我在 TreeTableCellSkin
class:
中找到了以下片段
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()
方法 class:
// 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 保持不变并且我的文本超出了...
关于在哪里或如何解决这个问题,您有什么想法吗?对任何方向的任何提示都表示赞赏。
非常感谢您!
编辑
看起来这个问题已经用 fx9 解决了,所以下面的解决方案只需要(并且可能)用 fx8
/编辑
好吧,我做了更多的挖掘,找到了(希望是正确的)解释和问题的肮脏解决方案。对解释感兴趣的人可以进一步阅读此 post,对于所有其他人,这里是我的肮脏的 quickfix。
脏但有效解决方案:
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);
}
所以我试图了解这个过程是如何工作的。看起来,TreeTableViewSkin
FX 中的 resizeColumnToFitContent
方法迭代显示的值,并为每个值构造一个新的 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
class使用与单元格关联的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
类型。
结果 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);
}
在我的应用程序中,我正在显示一个 TreeTableView 并希望增加缩进。我发现相应的 CSS 属性 -fx-indent
工作得很好。
但是,我现在正在努力计算首选宽度。
我希望该专栏能够完美地包装其内容,并使用
Auto resize column to fit content
这可以使用默认缩进,但它似乎不包括我在计算中所做的更改。通过源代码挖掘,我在 TreeTableCellSkin
class:
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()
方法 class:
// 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 保持不变并且我的文本超出了...
关于在哪里或如何解决这个问题,您有什么想法吗?对任何方向的任何提示都表示赞赏。
非常感谢您!
编辑
看起来这个问题已经用 fx9 解决了,所以下面的解决方案只需要(并且可能)用 fx8
/编辑
好吧,我做了更多的挖掘,找到了(希望是正确的)解释和问题的肮脏解决方案。对解释感兴趣的人可以进一步阅读此 post,对于所有其他人,这里是我的肮脏的 quickfix。
脏但有效解决方案:
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);
}
所以我试图了解这个过程是如何工作的。看起来,TreeTableViewSkin
FX 中的 resizeColumnToFitContent
方法迭代显示的值,并为每个值构造一个新的 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
class使用与单元格关联的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
类型。
结果 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);
}