我正在寻找一种方法来调整 TableView 中 TableColumn 的大小,以便在每个单元格中都能看到所有内容(即不截断)。
我注意到双击列分隔符会自动调整列的宽度以适应其单元格内容。是否有办法以编程方式触发此操作?
我正在寻找一种方法来调整 TableView 中 TableColumn 的大小,以便在每个单元格中都能看到所有内容(即不截断)。
我注意到双击列分隔符会自动调整列的宽度以适应其单元格内容。是否有办法以编程方式触发此操作?
在浏览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方法并使其公开来实现。
按照@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();
}
}
}
});
}
}
JavaFX当前版本(例如15-ea+1)会调整表格列的大小,如果尚未设置prefWidth属性(或者将其设置为80.0F,请参见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);
}
}
您可以使用 tableView.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY);
如果TableView.UNCONSTRAINED_RESIZE_POLICY
不能满足您的需求,您还可以尝试在两种策略TableView.CONSTRAINED_RESIZE_POLICY
和TableView.UNCONSTRAINED_RESIZE_POLICY
之间切换。
这里有一个有用的链接。
requestLayout()
。结果是每一列都被截断了。 - jckdnk111