【问题标题】:javafx column in tableview auto fit sizetableview 中的 javafx 列自动调整大小
【发布时间】:2013-01-17 00:43:42
【问题描述】:

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

【问题讨论】:

标签: javafx-2 javafx


【解决方案1】:

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

但我意识到,当我点击表头上的 divider 时,它会根据我的需要调整大小以适应内容。于是钻研JavaFX源码,终于在TableViewSkin中找到了resizeColumnToFitContent方法,但它是protected方法,我们可以通过反射解决: p>

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

之后调用这个函数

【讨论】:

  • 我找不到或导入 TableViewSkin 类。我正在使用 Java 8,但这个类似乎不存在。
  • @Spen:我用的是java 8,我可以看到,你能检查一下jfxrt.jar是否正确导入了吗?
  • 对我来说,这不会调整列的大小,只是让用户无法调整它们的大小
  • 方法“resizeColumnToFitContent”已移至 javafx.scene.control.skin.TableSkinUtils。
  • 在 JavaFx 16 中,方法 resizeColumnToFitContent 现在位于 javafx.scene.control.skin.TableColumnHeader
【解决方案2】:

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

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】:

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

    例子:

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

    【讨论】:

    • 值得注意的是,它会阻止用户调整列的大小。
    • 我使用的是 javafx 11.0.2,这个解决方案对我不起作用,它只是阻止调整大小,但列没有调整大小以适应内容..
    【解决方案4】:

    如果您希望只有一列填充表格的剩余宽度,我找到了一个非常简单的解决方案,它很短并且不需要上面描述的 hacky 反射解决方案:

    DoubleBinding usedWidth = columnA.widthProperty().add(columnB.widthProperty()).add(columnC.widthProperty());
    
    fillingColumn.prefWidthProperty().bind(tableView.widthProperty().subtract(usedWidth));
    

    【讨论】:

      【解决方案5】:

      或者简而言之:

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

      【讨论】:

        【解决方案6】:

        我在这个问题上使用了其他解决方案,效果很好。但是,这样做的缺点是当 TableView 的宽度大于 TableColumns 的所需宽度时。我创建了一个 hack 来解决这个问题,它工作正常:

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

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

        【讨论】:

        • 没有AtomicDouble这样的东西;使用DoubleAdder
        【解决方案7】:

        这是我找到的方式:

        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
        

        【讨论】:

          【解决方案8】:

          此代码自动调整所有列宽与表格宽度的关系比例,
          而当表格宽度小于x时,它可以将第一列宽度固定为给定值

           // 概括列宽相对于表格宽度的比例,
                  // 你不需要放置与像素相关的值,如果你愿意,你可以使用小的浮点数,
                  // 因为这里重要的是每列宽度的相对比例:
          
                  final float[] widths = { 1.2f, 2f, 0.8f };//定义每列的关系宽度
          
                  // 第一列是否应该固定
                  最终布尔 fixFirstColumm = true;
          
                  // 当表格宽度小于时,修复第一列的宽度:
                  最终浮动 fixOnTableWidth = 360; //像素
          
                  // 计算宽度的总和
                  浮点数 = 0;
                  对于(双我:宽度){
                      总和 += 我;
                  }
          
                  // 计算第一列相对于所有列比例总和的比例
                  浮动 firstColumnProportion = 宽度 [0] / 总和;
          
                  // 计算第一列的拟合固定宽度,你可以根据需要改变它,但它会跳转到这个宽度
                  最终浮动 firstColumnFixSize = fixOnTableWidth * firstColumnProportion;
          
                  // 设置列的宽度
                  for (int i = 0; i () {
                              @覆盖
                              公共无效更改(ObservableValue arg0,编号oldTableWidth,编号newTableWidth){
          
                                  if (newTableWidth.intValue()  x 则读取自动调整大小绑定
                                      table.getColumns().get(0).prefWidthProperty()
                                              .bind(table.widthProperty().multiply(firstColumnProportion));
                                  }
          
                              }
                          });
                      }
                  }
          

          建议将代码放在单独的 TableAutoresizeModel 类中,在那里您可以处理进一步的计算,例如在隐藏列时添加侦听器...

          【讨论】:

            【解决方案9】:

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

            这是通过监听表头 (TableHeaderRow) 上的点击事件来实现的。当发生双击时,通过匹配鼠标事件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());
                });
            }
            

            【讨论】:

              【解决方案10】:

              我已经为 TreeTableView 实现了一个解决方案。它仍在进化中,但现在显示出可喜的结果。以下是解决方案的说明。

              在控件外观类中,我向控件子控件添加了 TreeTableView 和一个不可见的 VBox。单元工厂向目标 TreeTableColumn 提供派生单元。派生单元格包裹一个Label节点,该节点根据空属性添加或删除到不可见的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);
              

              愉快的编码

              【讨论】:

                【解决方案11】:

                @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
                        )
                    }
                }
                

                【讨论】:

                  【解决方案12】:

                  这将根据字体和文本设置列的最小宽度,以便列名不会被裁剪。

                  public static void setDataTableMinColumnWidth(TableView<?> dataTable)
                      {
                          for (Node columnHeader : dataTable.lookupAll(".column-header"))
                          {
                              var columnString = columnHeader.getId();
                              if (columnString != null)
                              {
                                  for (Node columnHeaderLabel : columnHeader.lookupAll(".label"))
                                  {
                                      var tableColumn = dataTable.getColumns()
                                                                 .stream()
                                                                 .filter(x -> x.getId()
                                                                               .equals(columnString))
                                                                 .findFirst();
                                      if (columnHeaderLabel instanceof Label && tableColumn.isPresent())
                                      {
                                          var label = (Label) columnHeaderLabel;
                                          /* calc text width based on font */
                                          var theText = new Text(label.getText());
                                          theText.setFont(label.getFont());
                                          var width = theText.getBoundsInLocal()
                                                             .getWidth();
                                          /*
                                           * add 10px because of paddings/margins for the button
                                           */
                                          tableColumn.get()
                                                     .setMinWidth(width + 10);
                                      }
                                  }
                              }
                          }
                      }
                  

                  使用方法:

                  dataTable.needsLayoutProperty()
                               .addListener((obs, o, n) -> setDataTableMinColumnWidth(dataTable));
                  

                  对于Columns,需要先设置id属性:

                      TableColumn<BundImportTask, String> columnTask = new TableColumn<>("My Column");
                      columnTask.setId("MyColumnId");
                      columnTask.setCellValueFactory(data -> new SimpleStringProperty(data.getValue()
                                                                                          .fileName()));
                  

                  【讨论】:

                    【解决方案13】:

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

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

                    “自定义调整大小”

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

                    【讨论】:

                      【解决方案14】:
                      <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>
                      

                      【讨论】:

                      • CONSTRAINED_RESIZE_POLICY
                      • 请始终将您的答案放在上下文中,而不仅仅是粘贴代码。有关详细信息,请参阅here
                      【解决方案15】:
                      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);
                      }
                      

                      【讨论】:

                      • 请为您的解决方案添加评论;仅由代码组成的答案不被认为是好的答案。
                      猜你喜欢
                      • 2013-10-25
                      • 2014-10-31
                      • 2020-05-07
                      • 1970-01-01
                      • 2016-10-04
                      • 2017-04-23
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多