【问题标题】:JavaFX TreeView TreeColumn auto-size to fit contentJavaFX TreeView TreeColumn 自动调整大小以适应内容
【发布时间】:2014-07-02 14:39:42
【问题描述】:

一段时间以来,我一直在尝试弄清楚如何自动调整 TreeView 列的大小以适应其内容。以前有人问过这个问题,但反应很少,不能接受。

[javafx column in tableview auto fit size

奇怪的是,我正在寻找的行为正是当您双击 TableView 中的列调整大小行时发生的情况。但是,在查看 JavaFX 源代码时,我一直无法弄清楚这是如何发生的。

有谁知道在 JavaFX TableView/TableColumn/TableColumnHeader 中如何/在哪里处理列调整大小行上的双击行为?

如果可能,我只想让列在第一次渲染时自动执行双击调整大小行时发生的情况。这还不是可用的预先创建的列大小约束策略之一,我无法理解。

【问题讨论】:

  • 设法在 NestedTableColumnHeader 中找到它。自然地,该实现深埋在 cmets 中带有“naive”一词的隐藏的皮肤代码中。惊人的。 :-/
  • 我能够通过挖掘TableView皮肤和反映TableColumnHeader类的组合来调用TableColumnHeader的方法。它不漂亮,但它确实有效。
  • 怎么样?我也挖掘了代码并发现了同样的东西,但没有看到任何简单的方法来调用它。您为什么不发布一段代码以获得答案。
  • 一有机会我会发布一段代码。问题是当前的解决方案分布在几个文件中,因为它包括使用类方法反射来“破解”受保护的方法。如果不创建特殊示例,则不漂亮且不容易发布。

标签: resize javafx constraints tableview tablecolumn


【解决方案1】:

这是我为 JavaFX 2.2 提出的最终解决方案,它现在也考虑了列标题的宽度:

/**
 * Auto-sizes table view columns to fit its contents.
 * 
 * @note This is not a column resize policy and does not prevent manual
 *       resizing after this method has been called.
 * @param tableView
 *            The table view in which to resize all columns.
 */
public static void autoSizeTableViewColumns(final TableView<?> tableView)
{
    autoSizeTableViewColumns(tableView, -1, -1);
}

/**
 * Auto-sizes table view columns to fit its contents.
 * 
 * @note This is not a column resize policy and does not prevent manual
 *       resizing after this method has been called.
 *       
 * @param tableView
 *            The table view in which to resize all columns.
 * @param minWidth
 *            Minimum desired width of text for all columns.
 * @param maxWidth
 *            Maximum desired width of text for all columns.
 */
public static void autoSizeTableViewColumns(final TableView<?> tableView, int minWidth, int maxWidth)
{
    TableViewSkin<?> skin = (TableViewSkin<?>) tableView.getSkin();

    if (skin == null)
    {
        AppLogger.getLogger().warning(tableView + " skin is null.");
        return;
    }

    TableHeaderRow headerRow = skin.getTableHeaderRow();
    NestedTableColumnHeader rootHeader = headerRow.getRootHeader();
    for (Node node : rootHeader.getChildren())
    {
        if (node instanceof TableColumnHeader)
        {
            TableColumnHeader columnHeader = (TableColumnHeader) node;
            try
            {
                autoSizeTableViewColumn(columnHeader, minWidth, maxWidth, -1);
            }
            catch (Throwable e)
            {
                e = e.getCause();
                AppLogger.getLogger().log(Level.WARNING, "Unable to automatically resize tableView column.", e);
            }
        }
    }
}

/**
 * Auto-sizes table view columns to fit its contents.
 * 
 * @note This is not a column resize policy and does not prevent manual
 *       resizing after this method has been called.
 *       
 * @param col
 *            The column to resize.
 * @param minWidth
 *            Minimum desired width of text for this column. Use -1 for no minimum
 *            width.
 * @param maxWidth
 *            Maximum desired width of text for this column. Use -1 for no maximum
 *            width.
 * @param maxRows
 *            Maximum number of rows to examine for auto-resizing. Use -1
 *            for all rows.
 */
public static void autoSizeTableViewColumn(TableColumn<?,?> column, int minWidth, int maxWidth, int maxRows)
{
    TableView<?> tableView = column.getTableView();
    TableViewSkin<?> skin = (TableViewSkin<?>) tableView.getSkin();

    if (skin == null)
    {
        AppLogger.getLogger().warning(tableView + " skin is null.");
        return;
    }

    TableHeaderRow headerRow = skin.getTableHeaderRow();
    NestedTableColumnHeader rootHeader = headerRow.getRootHeader();
    for (Node node : rootHeader.getChildren())
    {
        if (node instanceof TableColumnHeader)
        {
            TableColumnHeader columnHeader = (TableColumnHeader) node;
            if(columnHeader.getTableColumn().equals(column))
            {
                autoSizeTableViewColumn(columnHeader, minWidth, maxWidth, maxRows);
            }
        }
    }
}

/**
 * Auto-sizes a table view column to fit its contents.
 * 
 * @note This is not a column resize policy and does not prevent manual
 *       resizing after this method has been called.
 * 
 * @param col
 *            The column to resize.
 * @param minWidth
 *            Minimum desired width of text for this column. Use -1 for no minimum
 *            width.
 * @param maxWidth
 *            Maximum desired width of text for this column. Use -1 for no maximum
 *            width.
 * @param maxRows
 *            Maximum number of rows to examine for auto-resizing. Use -1
 *            for all rows.
 */
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void autoSizeTableViewColumn(TableColumnHeader header, int minWidth, int maxWidth, int maxRows)
{
    TableColumn<?, ?> col = header.getTableColumn();
    if(col != null)
    {
        List<?> items = col.getTableView().getItems();
        if (items == null || items.isEmpty())
            return;

        Callback cellFactory = col.getCellFactory();
        if (cellFactory == null)
            return;

        TableCell cell = (TableCell) cellFactory.call(col);
        if (cell == null)
            return;

        // set this property to tell the TableCell we want to know its actual
        // preferred width, not the width of the associated TableColumn
        cell.getProperties().put("deferToParentPrefWidth", Boolean.TRUE);

        // determine cell padding
        double padding = 10;
        Node n = cell.getSkin() == null ? null : cell.getSkin().getNode();
        if (n instanceof Region)
        {
            Region r = (Region) n;
            padding = r.getInsets().getLeft() + r.getInsets().getRight();
        }

        int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);

        double desiredWidth = 0;

        // Check header
        Label headerLabel = (Label) header.lookup(".label");
        String headerText = headerLabel.getText();
        if (!headerLabel.getContentDisplay().equals(ContentDisplay.GRAPHIC_ONLY) && headerText != null)
        {
            Text text = new Text(headerLabel.getText());
            text.setFont(headerLabel.getFont());
            desiredWidth += text.getLayoutBounds().getWidth() + headerLabel.getLabelPadding().getLeft() + headerLabel.getLabelPadding().getRight();
        }

        Node headerGraphic = headerLabel.getGraphic();
        if((headerLabel.getContentDisplay().equals(ContentDisplay.LEFT) || headerLabel.getContentDisplay().equals(ContentDisplay.RIGHT)) && headerGraphic != null)
        {
            desiredWidth += headerGraphic.getLayoutBounds().getWidth();
        }

        // Handle minimum width calculations
        // Use a "w" because it is typically the widest character
        Text minText = new Text(StringUtils.repeat("W", Math.min(0, minWidth)));
        minText.setFont(headerLabel.getFont());

        // Check rows
        double minPxWidth = 0;
        for (int row = 0; row < rows; row++)
        {
            cell.updateTableColumn(col);
            cell.updateTableView(col.getTableView());
            cell.updateIndex(row);

            // Handle minimum width calculations
            // Just do this once
            if(row == 0)
            {
                String oldText = cell.getText();
                // Use a "w" because it is typically the widest character
                cell.setText(StringUtils.repeat("W", Math.max(0, minWidth)));

                header.getChildren().add(cell);
                cell.impl_processCSS(false);
                minPxWidth = cell.prefWidth(-1);
                header.getChildren().remove(cell);

                cell.setText(oldText);
            }

            if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null)
            {
                header.getChildren().add(cell);
                cell.impl_processCSS(false);
                desiredWidth = Math.max(desiredWidth, cell.prefWidth(-1));
                desiredWidth = Math.max(desiredWidth, minPxWidth);
                header.getChildren().remove(cell);
            }
        }

        desiredWidth = desiredWidth + padding;

        if(maxWidth > 0)
        {
            desiredWidth = Math.min(maxWidth, desiredWidth);
        }

        col.impl_setWidth(desiredWidth);
    }
}

请记住,这是“黑客攻击”受保护的方法,可能会随着 JavaFX 更新随时更改并破坏该方法。我相信这在 JavaFX 8 中已经发生了变化。这也可能会根据您的应用程序的签名和部署方式而中断。

它并不漂亮,但是 JavaFX 隐藏了这个功能,所以除非你想重新实现整个东西,否则这是我找到的最好的方法。

【讨论】:

  • 谢谢,这是我不知道的 setAccessible。他们应该公开。
  • 同意他们应该公开。这不是他们第一次仅仅因为称之为“可能很慢”而隐藏某些东西。 Oracle,你为什么不让我决定是否要过度优化速度?
  • 你有没有想出任何新的东西来处理 JavaFX 8 中的这个问题?
  • 不,我正在处理的项目已被搁置,因此尚未过渡到 JavaFX 8。
  • StringUtils.repeat("W", Math.min(0, minWidth)) - 这不应该是最大值吗?
【解决方案2】:

您的代码中缺少一些部分,例如未声明 obj。我也找不到您在该课程的任何地方使用的方法。我正在使用 1.8.0_20-ea-b05 ,或者 -version 告诉我。我在 TableViewSkin 中找到了类似的方法。

public static void autoSizeTableViewColumns(final TableView<?> tableView) {
    TableViewSkin<?> skin = (TableViewSkin<?>) tableView.getSkin();
    TableHeaderRow headerRow = skin.getTableHeaderRow();
    NestedTableColumnHeader rootHeader = headerRow.getRootHeader();
    for (TableColumnHeader columnHeader : rootHeader.getColumnHeaders()) {
        try {
            TableColumn<?, ?> column = (TableColumn<?, ?>) columnHeader.getTableColumn();
            if (column != null) {
                Method method = skin.getClass().getDeclaredMethod("resizeColumnToFitContent", TableColumn.class, int.class);
                method.setAccessible(true);
                method.invoke(skin,column, 30);
            }
        } catch (Throwable e) {
            e = e.getCause();
            e.printStackTrace(System.err);
        }
    }
}

【讨论】:

  • 是的,正如我所说,这些是私有方法,所以当你切换版本时,所有的赌注都是关闭的。我正在使用 JavaFX 2.2。这段代码是我真实代码的剪切和粘贴混搭,所以我错过了 obj 参考。那应该是“列”。我的答案已更新。
  • 顺便说一句,您应该始终检查以确保皮肤不会返回 null。如果您的视图尚未渲染,则未设置皮肤。您的版本容易受到 NullPointerExceptions 的影响。
  • 当前方法的另一个问题是它不考虑列标题的宽度——只考虑列内容。
【解决方案3】:

在 JavaFX8 上适合我的解决方案:

columnResizePolciy 设置为CONSTRAINED_RESIZE_POLICY

<TableView fx:id="table" >
    <columnResizePolicy>
        <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
    </columnResizePolicy>
</TableView>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-21
    • 2013-06-05
    • 2021-09-16
    • 2011-11-03
    • 2011-08-20
    • 1970-01-01
    • 2021-12-17
    • 2015-12-27
    相关资源
    最近更新 更多