tableview中的javafx列自动调整大小

时间:2013-02-01 16:31:00

标签: javafx-2 javafx

afaik javafx中的TableView有两个列调整大小策略:CONSTRAINED_RESIZE_POLICY和UNCONSTRAINED_RESIZE_POLICY,但我想要调整列的大小以适应他们的单元格内容 我认为这是其他平台中的一个简单问题(如C#中的datagridview),但无法解决

15 个答案:

答案 0 :(得分:25)

3年后,我再次回到这个问题,一些建议正在计算每个单元格中数据文本的大小(根据字体大小,字体系列,填充......这很复杂)。

但我意识到当我点击表格标题上的分隔符时,它的大小适合我想要的内容。所以我深入研究了JavaFX源代码,我终于在 TableViewSkin 中找到了 resizeColumnToFitContent 方法,但它是受保护的方法,我们可以通过反思来解决:

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();
                    }
                }
            }
        });
    }
}

请注意,我们称之为“ tableView.getItems()”,因此我们必须在 setItems()

之后调用此函数

答案 1 :(得分:5)

我认为只要覆盖一个返回true的回调函数就可以解决你的问题,它将禁用重新调整列的大小,并且所有列都将重新调整大小以适合其单元格的内容。

示例:

TableView<String[]> table = new TableView<>();
table.setColumnResizePolicy(new Callback<TableView.ResizeFeatures, Boolean>() {
  @Override
  public Boolean call(ResizeFeatures p) {
     return true;
  }
});

答案 2 :(得分:5)

在测试了之前的解决方案后,我终于找到了一个适合我的解决方案。 所以这是我的(在将数据插入表后调用方法):

public static void autoResizeColumns( TableView<?> table )
{
    //Set the right policy
    table.setColumnResizePolicy( TableView.UNCONSTRAINED_RESIZE_POLICY);
    table.getColumns().stream().forEach( (column) ->
    {
        //Minimal width = columnheader
        Text t = new Text( column.getText() );
        double max = t.getLayoutBounds().getWidth();
        for ( int i = 0; i < table.getItems().size(); i++ )
        {
            //cell must not be empty
            if ( column.getCellData( i ) != null )
            {
                t = new Text( column.getCellData( i ).toString() );
                double calcwidth = t.getLayoutBounds().getWidth();
                //remember new max-width
                if ( calcwidth > max )
                {
                    max = calcwidth;
                }
            }
        }
        //set the new max-widht with some extra space
        column.setPrefWidth( max + 10.0d );
    } );
}

答案 3 :(得分:2)

或简而言之:

// automatically adjust width of columns depending on their content
configAttributeTreeTable.setColumnResizePolicy((param) -> true );

答案 4 :(得分:2)

我在这个问题上使用过其他解决方案,效果还不错。但是,这方面的缺点是TableView的宽度大于TableColumns的所需宽度。我已经创建了一个解决这个问题的黑客,它运行正常:

orderOverview.setColumnResizePolicy((param) -> true );
Platform.runLater(() -> FXUtils.customResize(orderOverview));

其中FXUtils.customResize()创建如下:

public static void customResize(TableView<?> view) {

    AtomicDouble width = new AtomicDouble();
    view.getColumns().forEach(col -> {
        width.addAndGet(col.getWidth());
    });
    double tableWidth = view.getWidth();

    if (tableWidth > width.get()) {
        TableColumn<?, ?> col = view.getColumns().get(view.getColumns().size()-1);
        col.setPrefWidth(col.getWidth()+(tableWidth-width.get()));
    }

}

我希望这对其他人也有帮助!

答案 5 :(得分:2)

如果你想只有一个列填充表格的剩余宽度,我找到了一个非常直接的解决方案,这个解决方案很简短,不需要上面描述的hacky反射解决方案:

DoubleBinding usedWidth = columnA.widthProperty().add(columnB.widthProperty()).add(columnC.widthProperty());

fillingColumn.prefWidthProperty().bind(tableView.widthProperty().subtract(usedWidth));

答案 6 :(得分:0)

此代码以与表宽度相关的比例自动调整所有列宽,
当表格宽度低于x

        // To generalize the columns width proportions in relation to the table width,
        // you do not need to put pixel related values, you can use small float numbers if you wish,
        // because it's the relative proportion of each columns width what matters here:

        final float[] widths = { 1.2f, 2f, 0.8f };// define the relational width of each column 

        // whether the first column should be fixed
        final boolean fixFirstColumm = true; 

        // fix the first column width when table width is lower than:
        final float fixOnTableWidth = 360; //pixels 

        // calulates sum of widths
        float sum = 0;
        for (double i : widths) {
            sum += i;
        }

        // calculates the fraction of the first column proportion in relation to the sum of all column proportions
        float firstColumnProportion = widths[0] / sum;

        // calculate the fitting fix width for the first column, you can change it by your needs, but it jumps to this width
        final float firstColumnFixSize = fixOnTableWidth * firstColumnProportion;

        // set the width to the columns
        for (int i = 0; i < widths.length; i++) {
            table.getColumns().get(i).prefWidthProperty().bind(table.widthProperty().multiply((widths[i] / sum)));
            // ---------The exact width-------------^-------------^
    if (fixFirstColumm)
            if (i == 0) {
                table.widthProperty().addListener(new ChangeListener<Number>() {
                    @Override
                    public void changed(ObservableValue<? extends Number> arg0, Number oldTableWidth, Number newTableWidth) {

                        if (newTableWidth.intValue() <= fixOnTableWidth) {

                            // before you can set new value to column width property, need to unbind the autoresize binding
                            table.getColumns().get(0).prefWidthProperty().unbind();
                            table.getColumns().get(0).prefWidthProperty().setValue(firstColumnFixSize);

                        } else if (!table.getColumns().get(0).prefWidthProperty().isBound()) {

                            // than readd the autoresize binding if condition table.width > x
                            table.getColumns().get(0).prefWidthProperty()
                                    .bind(table.widthProperty().multiply(firstColumnProportion));
                        }

                    }
                });
            }
        }
时,它可以将第一列宽度固定为给定值

建议将代码放在一个单独的TableAutoresizeModel类中,在那里你可以处理进一步的计算,比如在隐藏列上添加监听器......

答案 7 :(得分:0)

public static void autoFillColumn(TableView<?> table, int col) {
    double width = 0;
    for (int i = 0; i < table.getColumns().size(); i++) {
        if (i != col) {
            width += table.getColumns().get(i).getWidth();
        }
    }
    table.getColumns().get(col).setPrefWidth(table.getWidth() - width);
}

答案 8 :(得分:0)

myTable.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
for(TableColumn column: myTable.getColumns())
{           
    column.setMinWidth(200);                                        
}

答案 9 :(得分:0)

<TableView fx:id="datalist" layoutX="30.0" layoutY="65.0" prefHeight="400.0" AnchorPane.bottomAnchor="100.0" AnchorPane.leftAnchor="30.0" AnchorPane.rightAnchor="30.0" AnchorPane.topAnchor="100.0">
        <columns>
            <TableColumn fx:id="number" minWidth="-1.0" prefWidth="-1.0" style="width: auto;" text="number" />
            <TableColumn fx:id="id" minWidth="-1.0" prefWidth="-1.0" text="id" />
            <TableColumn fx:id="name" minWidth="-1.0" prefWidth="-1.0" text="name" />
            <TableColumn fx:id="action" minWidth="-1.0" prefWidth="-1.0" text="todo" />
        </columns>
         **<columnResizePolicy>
            <TableView fx:constant="CONSTRAINED_RESIZE_POLICY" />
         </columnResizePolicy>**
      </TableView>

答案 10 :(得分:0)

我实现了一个解决方案,它比我在这里找到的解决方案要复杂得多,但是允许双击标题来调整特定列的大小,同时仍然允许用户调整列的大小手动。

这是通过侦听表头(TableHeaderRow)上的click事件来实现的。双击时,通过匹配鼠标事件X和Y找到特定的列标题。

注意:要执行此操作,必须为每个列设置一个ID。

// when skin is loaded (hence css), setup click listener on header to make column fit to content max width on double click
tableView.skinProperty().addListener((a, b, newSkin) -> {
    TableHeaderRow headerRow = (TableHeaderRow) tableView.lookup("TableHeaderRow");
    NestedTableColumnHeader headers = (NestedTableColumnHeader) (headerRow.getChildren().get(1));

    headerRow.setOnMouseClicked(evt -> {
        if (evt.getClickCount() != 2 || evt.getButton() != MouseButton.PRIMARY) return;

        // find the header column that contains the click
        for (TableColumnHeader header : headers.getColumnHeaders()) {
            if (header.contains(header.parentToLocal(evt.getX(), evt.getY()))) {
                fitColumnWidthToContent(header.getId());
            }
        }
        evt.consume();
    });
});

负责调整大小的方法如下:

 private void fitColumnWidthToContent (String colId) {
    // find column matching id
    TableColumn column = null;

    for (TableColumn tempCol : tableView.getColumns()) {
        if (tempCol.getId().equals(colId)) {
            column = tempCol;
            break;
        }
    }

    if (column == null) {
        throw new IllegalStateException("Column ID doesn't match any actual column");
    }

    // set default width to column header width
    Text text = new Text(column.getText());
    double max = text.getLayoutBounds().getWidth();

    for (int i = 0; i < tableView.getItems().size(); i++ ) {
        if (column.getCellData(i) == null) continue;

        text = new Text(column.getCellData(i).toString());
        double textWidth = text.getLayoutBounds().getWidth();

        if (textWidth > max) {
            max = textWidth;
        }
    }

    column.setPrefWidth(max + 12);
}

我希望这对任何人都有用。

为了也允许手动调整大小,有必要在表初始化上添加更多代码:

// listen to width changes in columns and set to pref width (otherwise if for example width changes because of
// user resizing the column, applying the old pref width won't work because it stayed the same)
for (TableColumn col : tableView.getColumns()) {
    col.widthProperty().addListener((obs, oldVal, newVal) -> {
        col.setPrefWidth(newVal.doubleValue());
    });
}

答案 11 :(得分:0)

我已经为 TreeTableView 实施了一个解决方案。它仍在进化中,但现在已经显示出有希望的结果。以下是解决方案的描述。

在控件外观类中,我向控件子项添加了 TreeTableView 和一个不可见的 VBox。细胞工厂向目标 TreeTableColumn 提供派生的细胞。派生的单元格包裹一个标签节点,该节点根据空属性添加或删除到不可见的VBox,并根据单元格宽度设置其prefWidth。细胞利用:

getProperties().put(Properties.DEFER_TO_PARENT_PREF_WIDTH, Boolean.TRUE)

我按如下方式覆盖单元格的 computePrefWidth() 方法:

@Override
protected double computePrefWidth(double height) {

    return Double.max(_box.prefWidth(-1.0), super.computePrefWidth(height) + 24.0);

}

Vbox 宽度属性绑定到 TreeTableColumn 的 prefWidth。这是调整列标题大小所必需的。

值得注意的是,目前为了简化解决方案的开发,这种方法在禁用内置排序、排序和调整大小功能的情况下效果很好。即。

_nameColumn = new TreeTableColumn<>("Name");
_nameColumn.setResizable(false);
_nameColumn.setReorderable(false);
_nameColumn.setSortable(false);

快乐编码

答案 12 :(得分:0)

@HarleyDavidson 在 kotlin 中的回答

val String.fxWidth: Double
    get() = Text(this).layoutBounds.width

//  call the method after inserting the data into table
fun <T> TableView<T>.autoResizeColumns() {
    columnResizePolicy = TableView.UNCONSTRAINED_RESIZE_POLICY
    columns.forEach { column ->
        column.setPrefWidth(
            (((0 until items.size).mapNotNull {
                column.getCellData(it)
            }.map {
                it.toString().fxWidth
            }.toMutableList() + listOf(
                column.text.fxWidth
            )).maxOrNull() ?: 0.0) + 10.0
        )
    }
}

答案 13 :(得分:0)

这是我找到的方法:

tableview.setColumnResizePolicy( TableView.CONSTRAINED_RESIZE_POLICY );
idCol.setMaxWidth( 1f * Integer.MAX_VALUE * 50 ); // 50% width
nameCol.setMaxWidth( 1f * Integer.MAX_VALUE * 30 ); // 30% width
ageCol.setMaxWidth( 1f * Integer.MAX_VALUE * 20 ); // 20% width

答案 14 :(得分:-1)

经过长时间的研究。最佳解决方案是......

tblPlan.setColumnResizePolicy((param) -> true );
Platform.runLater(() -> customResize(tblPlan));
  

&#34;自定义调整大小&#34;

public void customResize(TableView<?> view) {

        AtomicLong width = new AtomicLong();
        view.getColumns().forEach(col -> {
            width.addAndGet((long) col.getWidth());
        });
        double tableWidth = view.getWidth();

        if (tableWidth > width.get()) {
            view.getColumns().forEach(col -> {
                col.setPrefWidth(col.getWidth()+((tableWidth-width.get())/view.getColumns().size()));
            });
        }
    }