【问题标题】:JavaFX TableColumn resize to fit cell contentJavaFX TableColumn 调整大小以适合单元格内容
【发布时间】:2014-06-10 15:43:57
【问题描述】:

我正在寻找一种方法来调整 TableView 中 TableColumn 的大小,以便每个单元格中的所有内容都可见(即没有截断)。

我注意到双击列分隔符会自动使列适应其单元格的内容。有没有办法以编程方式触发?

【问题讨论】:

  • 您是否在场景构建器中创建了视图
  • 是的,视图是在场景构建器中创建的。
  • 然后我的事情你不能让它在父扩展上扩展
  • 我认为它在哪里创建并不重要。我只想在表格充满内容时以编程方式强制调整大小。基本上我想避免单元格中的数据被截断的情况。
  • 嗨,您找到解决问题的方法了吗?我需要做同样的事情:p

标签: javafx tableview


【解决方案1】:

你可以使用tableView.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);

您也可以尝试在两种策略之间切换TableView.CONSTRAINED_RESIZE_POLICYTableView.UNCONSTRAINED_RESIZE_POLICY 以防仅TableView.UNCONSTRAINED_RESIZE_POLICY 不能满足您的需求。

Here's 一个有用的链接。

【讨论】:

  • 不幸的是,这只会为用户触发的列调整大小设置策略。我希望以编程方式使列适合其内容(即在添加/更新/删除数据后自动调整列大小)。
  • 您可以做的是通过监听器在这两个策略之间切换,然后重新绘制(重新布局)。
  • 这确实会调整列的大小,但会尝试将它们全部放入可视空间中(即尝试使其不需要水平滚动条的表格)。所以它实际上最终会截断所有内容。
  • 您可以在截断发生之前将策略设置为不受约束。
  • 其实我说的就是一开始把policy设置成constraint(因为没有数据),然后当数据变长的时候,就是我们把policy设置成unconstrained的时候。但无论如何, requestLayout 的假定行为是不截断表格。一定少了点什么。您可以尝试对代码进行更多修改。如果情况仍然没有改变,您可以再次在这里给我发消息。问候:D
【解决方案2】:

翻阅javafx源码,发现点击TableView列分隔符时实际调用的方法是

/*
 * FIXME: Naive implementation ahead
 * Attempts to resize column based on the pref width of all items contained
 * in this column. This can be potentially very expensive if the number of
 * rows is large.
 */
@Override protected void resizeColumnToFitContent(TableColumn<T, ?> tc, int maxRows) {
    if (!tc.isResizable()) return;

// final TableColumn<T, ?> col = tc;
    List<?> items = itemsProperty().get();
    if (items == null || items.isEmpty()) return;

    Callback/*<TableColumn<T, ?>, TableCell<T,?>>*/ cellFactory = tc.getCellFactory();
    if (cellFactory == null) return;

    TableCell<T,?> cell = (TableCell<T, ?>) cellFactory.call(tc);
    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 TableColumnBase
    cell.getProperties().put(TableCellSkin.DEFER_TO_PARENT_PREF_WIDTH, 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.snappedLeftInset() + r.snappedRightInset();
    } 

    int rows = maxRows == -1 ? items.size() : Math.min(items.size(), maxRows);
    double maxWidth = 0;
    for (int row = 0; row < rows; row++) {
        cell.updateTableColumn(tc);
        cell.updateTableView(tableView);
        cell.updateIndex(row);

        if ((cell.getText() != null && !cell.getText().isEmpty()) || cell.getGraphic() != null) {
            getChildren().add(cell);
            cell.applyCss();
            maxWidth = Math.max(maxWidth, cell.prefWidth(-1));
            getChildren().remove(cell);
        }
    }

    // dispose of the cell to prevent it retaining listeners (see RT-31015)
    cell.updateIndex(-1);

    // RT-36855 - take into account the column header text / graphic widths.
    // Magic 10 is to allow for sort arrow to appear without text truncation.
    TableColumnHeader header = getTableHeaderRow().getColumnHeaderFor(tc);
    double headerTextWidth = Utils.computeTextWidth(header.label.getFont(), tc.getText(), -1);
    Node graphic = header.label.getGraphic();
    double headerGraphicWidth = graphic == null ? 0 : graphic.prefWidth(-1) + header.label.getGraphicTextGap();
    double headerWidth = headerTextWidth + headerGraphicWidth + 10 + header.snappedLeftInset() + header.snappedRightInset();
    maxWidth = Math.max(maxWidth, headerWidth);

    // RT-23486
    maxWidth += padding;
    if(tableView.getColumnResizePolicy() == TableView.CONSTRAINED_RESIZE_POLICY) {
        maxWidth = Math.max(maxWidth, tc.getWidth());
    }

    tc.impl_setWidth(maxWidth);
}

声明在

com.sun.javafx.scene.control.skin.TableViewSkinBase

方法签名

protected abstract void resizeColumnToFitContent(TC tc, int maxRows)

由于它是受保护的,你不能从例如它调用它。 tableView.getSkin(),但您始终可以扩展 TableViewSkin 仅覆盖 resizeColumnToFitContent 方法并将其公开。

【讨论】:

【解决方案3】:

作为@Tomasz 的建议,我通过反思解决:

import com.sun.javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.Skin;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class GUIUtils {
    private static Method columnToFitMethod;

    static {
        try {
            columnToFitMethod = TableViewSkin.class.getDeclaredMethod("resizeColumnToFitContent", TableColumn.class, int.class);
            columnToFitMethod.setAccessible(true);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static void autoFitTable(TableView tableView) {
        tableView.getItems().addListener(new ListChangeListener<Object>() {
            @Override
            public void onChanged(Change<?> c) {
                for (Object column : tableView.getColumns()) {
                    try {
                        columnToFitMethod.invoke(tableView.getSkin(), column, -1);
                    } catch (IllegalAccessException | InvocationTargetException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
    }
}

【讨论】:

  • 很好的解决方案!完美运行。
  • 在 JDK 9 上,我得到一个 NoSuchMethodException :(
  • 对我不起作用,只是让我无法手动调整列的大小
  • @AxelKennedal 您使用哪个 Java 版本?
  • 如果 tableView.getSkin() 在我的 Java 8 托管程序中返回 null,TableViewSkin "columnToFitMethod" 方法会引发 NullPointerException,这对我来说毫无用处。另外,我不会依赖剩余的私人遗留太阳课程......
【解决方案4】:

如果 prefWidth 从未设置(或设置为 80.0F,请参阅 TableColumnHeader enter link description here),当前版本的 JavaFX(例如 15-ea+1)会调整表列的大小。

【讨论】:

    【解决方案5】:

    在 Java 16 中,我们可以扩展 TableView 以使用自定义 TableViewSkin,而后者又使用自定义 TableColumnHeader

    class FitWidthTableView<T> extends TableView<T> {
        
        public FitWidthTableView() {
            setSkin(new FitWidthTableViewSkin<>(this));
        }
    
        public void resizeColumnsToFitContent() {
            Skin<?> skin = getSkin();
            if (skin instanceof FitWidthTableViewSkin<?> tvs) tvs.resizeColumnsToFitContent();
        }
    }
    
    class FitWidthTableViewSkin<T> extends TableViewSkin<T> {
        
        public FitWidthTableViewSkin(TableView<T> tableView) {
            super(tableView);
        }
    
        @Override
        protected TableHeaderRow createTableHeaderRow() {
            return new TableHeaderRow(this) {
                
                @Override
                protected NestedTableColumnHeader createRootHeader() {
                    return new NestedTableColumnHeader(null) {
                        
                        @Override
                        protected TableColumnHeader createTableColumnHeader(TableColumnBase col) {
                            return new FitWidthTableColumnHeader(col);
                        }
                    };
                }
            };
        }
    
        public void resizeColumnsToFitContent() {
            for (TableColumnHeader columnHeader : getTableHeaderRow().getRootHeader().getColumnHeaders()) {
                if (columnHeader instanceof FitWidthTableColumnHeader colHead) colHead.resizeColumnToFitContent(-1);
            }
        }
    }
    
    class FitWidthTableColumnHeader extends TableColumnHeader {
        
        public FitWidthTableColumnHeader(TableColumnBase col) {
            super(col);
        }
    
        @Override
        public void resizeColumnToFitContent(int rows) {
            super.resizeColumnToFitContent(-1);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2013-07-08
      • 1970-01-01
      • 1970-01-01
      • 2021-11-16
      • 2017-04-10
      • 2014-07-02
      • 2017-03-12
      • 2013-05-26
      相关资源
      最近更新 更多