JavaFX TableView with two fixed-columns and last o

2019-07-23 06:30发布

I'm trying to make a simple 3-column TableView:

  • One icon column containing a fixed size icon. This column must not be resizable, and must have a fixed size.
  • One text column with a predefined prefered size, which can be resized if needed.
  • One last column taking all available space.

Unfortunatly, this simple use case seems to be really complicated with Java FX 8. I tried the following, which should work according to my understanding of the documentation:

TableColumn<DebuggerItem, ImageView> iconColumn = new TableColumn<>("ICON");
TableColumn<DebuggerItem, String> typeColumn = new TableColumn<>("TEXT");
TableColumn<DebuggerItem, String> textColumn = new TableColumn<>("DATA");
setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY);
// Fixed size column
iconColumn.setPrefWidth(40);
iconColumn.setMinWidth(40);
iconColumn.setMaxWidth(40);
iconColumn.setResizable(false);
// Predefined preferred size of 100px
typeColumn.setPrefWidth(100);
getColumns().addAll(iconColumn, typeColumn, textColumn);

This results in the following TableView:

enter image description here

We can see that if the first column has a correct size, the second and the hird have the same size, which is not what I expected. The second column should be 100px wide, and the last one take the rest of the space.

What did I miss ?

2条回答
Juvenile、少年°
2楼-- · 2019-07-23 07:22

According to @kleopatra link, the solution is to use properties to compute last column width, and NOT use CONSTRAINED_RESIZE_POLICY :

LastColumnWidth = TableViewWidth - SUM(Other Columns Widths)

Which, according to my example give the following Java code:

TableColumn<DebuggerItem, ImageView> iconColumn = new TableColumn<>("ICON");
TableColumn<DebuggerItem, String> typeColumn = new TableColumn<>("TEXT");
TableColumn<DebuggerItem, String> textColumn = new TableColumn<>("DATA");
// Fixed size column
iconColumn.setPrefWidth(40);
iconColumn.setMinWidth(40);
iconColumn.setMaxWidth(40);
iconColumn.setResizable(false);
// Predefined preferred size of 100px
typeColumn.setPrefWidth(100);
// Automatic width for last column
textColumn.prefWidthProperty().bind(
    widthProperty().subtract(
       iconColumn.widthProperty()).subtract(
       typeColumn.widthProperty()).subtract(2)
);
getColumns().addAll(iconColumn, typeColumn, textColumn);

Please note that we need to substract 2 pixels to get the exact width, it's not clear why.

查看更多
够拽才男人
3楼-- · 2019-07-23 07:24

I created a more general solution, that pixel-perfectly calculates width of last column for TableViews that:

  • have any number of columns
  • possibly hide or show columns at runtime
  • possibly have custom insets
  • possibly have scrollbar, possibly showing and hiding at runtime.

Usage: bindLastColumnWidth(tableView);

Source:

public static <T> void bindLastColumnWidth (TableView<T> tableView) {
    List<TableColumn<T,?>> columns = tableView.getColumns();
    List<TableColumn<T,?>> columnsWithoutLast = columns.subList(0, columns.size() - 1);
    TableColumn lastColumn = columns.get(columns.size() - 1);

    NumberExpression expression = tableView.widthProperty();
    Insets insets = tableView.getInsets();
    expression = expression.subtract(insets.getLeft() + insets.getRight());

    for (TableColumn column : columnsWithoutLast) {
        NumberExpression columnWidth = Bindings.when(column.visibleProperty())
            .then(column.widthProperty())
            .otherwise(0);
        expression = expression.subtract(columnWidth);
    }

    ScrollBar verticalScrollBar = getScrollBar(tableView, Orientation.VERTICAL);
    if (verticalScrollBar != null) {
        NumberExpression scrollBarWidth = Bindings.when(verticalScrollBar.visibleProperty())
            .then(verticalScrollBar.widthProperty())
            .otherwise(0);
        expression = expression.subtract(scrollBarWidth);
    }

    expression = Bindings.max(lastColumn.getPrefWidth(), expression);

    lastColumn.prefWidthProperty().bind(expression);
}

private static ScrollBar getScrollBar (Node control, Orientation orientation) {
    for (Node node : control.lookupAll(".scroll-bar")) {
        if (node instanceof ScrollBar) {
            ScrollBar scrollBar = (ScrollBar)node;
            if (scrollBar.getOrientation().equals(orientation)) {
                return scrollBar;
            }
        }
    }
    return null;
}
查看更多
登录 后发表回答