0

When i double-click the right border of a column's header cell - it automatically adjust the width of a column. How can i do same programmatically?

Alexander Cyberman
  • 2,114
  • 3
  • 20
  • 21

4 Answers4

4

Finally I found the solution:

    TableViewSkin<?> skin = (TableViewSkin<?>) table.getSkin();
    TableHeaderRow headerRow = skin.getTableHeaderRow();
    NestedTableColumnHeader rootHeader = headerRow.getRootHeader();
    for (TableColumnHeader columnHeader : rootHeader.getColumnHeaders()) {
        try {
            TableColumn<?, ?> column = (TableColumn<?, ?>) columnHeader.getTableColumn();
            if (column != null) {
                Method method = skin.getClass().getDeclaredMethod("resizeColumnToFitContent", TableColumn.class, int.class);
                method.setAccessible(true);
                method.invoke(skin, column, 30);
            }
        } catch (Throwable e) {
            e = e.getCause();
            e.printStackTrace(System.err);
        }
    }

Javafx still crude. Many simple things need to do through deep ass...

Alexander Cyberman
  • 2,114
  • 3
  • 20
  • 21
4

(not only internal) API changed from fx8 to fx9:

  • TableViewSkin (along with all skin implementations) moved into public scope: now it would be okay to subclass and implement whatever additional functionality is needed
  • tableViewSkin.getTableHeaderRow() and tableHeaderRow.getRootHeader() changed from protected to package-private scope, the only way to legally access any is via lookup
  • implementation of resizeToFitContent moved from skin to a package-private utility class TableSkinUtils, no way to access at all
  • TableColumnHeader got a private doColumnAutoSize(TableColumnBase, int) that calls the utility method, provided the column's prefWidth has its default value 80. On the bright side: due to that suboptimal api we can grab an arbitrary header and auto-size any column

In code (note: this handles all visible leaf columns as returned by the TableView, nested or not - if you want to include hidden columns, you'll need to collect them as well)

/**
 * Resizes all visible columns to fit its content. Note that this does nothing if a column's 
 * prefWidth is != 80.
 * 
 * @param table
 */
public static void doAutoSize(TableView<?> table) {
    // good enough to find an arbitrary column header
    // due to sub-optimal api
    TableColumnHeader header = (TableColumnHeader) table.lookup(".column-header");
    if (header != null) {
        table.getVisibleLeafColumns().stream().forEach(column -> doColumnAutoSize(header, column));
    }
}

public static void doColumnAutoSize(TableColumnHeader columnHeader, TableColumn column) {
    // use your preferred reflection utility method 
    FXUtils.invokeGetMethodValue(TableColumnHeader.class, columnHeader, "doColumnAutoSize", 
            new Class[] {TableColumnBase.class, Integer.TYPE}, 
            new Object[] {column, -1});
}
kleopatra
  • 51,061
  • 28
  • 99
  • 211
1

Using the above excellent answer posted by Александр Киберман, I created a class to handle this and the issue of getSkin() == null:

import com.sun.javafx.scene.control.skin.NestedTableColumnHeader;
import com.sun.javafx.scene.control.skin.TableColumnHeader;
import com.sun.javafx.scene.control.skin.TableHeaderRow;
import com.sun.javafx.scene.control.skin.TableViewSkin;
import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;

import java.lang.reflect.Method;

// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
public class TableViewPlus extends TableView
{
  private boolean flSkinPropertyListenerAdded = false;

  // ---------------------------------------------------------------------------------------------------------------------
  public TableViewPlus()
  {
    super();

    this.setEditable(false);
  }

  // ---------------------------------------------------------------------------------------------------------------------
  public TableViewPlus(final ObservableList toItems)
  {
    super(toItems);

    this.setEditable(false);
  }

  // ---------------------------------------------------------------------------------------------------------------------
  public void resizeColumnsToFit()
  {
    if (this.getSkin() != null)
    {
      this.resizeColumnsPlatformCheck();
    }
    else if (!this.flSkinPropertyListenerAdded)
    {
      this.flSkinPropertyListenerAdded = true;

      // From https://stackoverflow.com/questions/38718926/how-to-get-tableheaderrow-from-tableview-nowadays-in-javafx
      // Add listener to detect when the skin has been initialized and therefore this.getSkin() != null.
      this.skinProperty().addListener((a, b, newSkin) -> this.resizeColumnsPlatformCheck());
    }

  }

  // ---------------------------------------------------------------------------------------------------------------------
  private void resizeColumnsPlatformCheck()
  {
    if (Platform.isFxApplicationThread())
    {
      this.resizeAllColumnsUsingReflection();
    }
    else
    {
      Platform.runLater(this::resizeAllColumnsUsingReflection);
    }
  }

  // ---------------------------------------------------------------------------------------------------------------------
  // From https://stackoverflow.com/questions/38090353/javafx-how-automatically-width-of-tableview-column-depending-on-the-content
  // Geesh. . . .
  private void resizeAllColumnsUsingReflection()
  {
    final TableViewSkin<?> loSkin = (TableViewSkin<?>) this.getSkin();
    // The skin is not applied till after being rendered. Which is happening with the About dialog.
    if (loSkin == null)
    {
      System.err.println("Skin is null");
      return;
    }

    final TableHeaderRow loHeaderRow = loSkin.getTableHeaderRow();
    final NestedTableColumnHeader loRootHeader = loHeaderRow.getRootHeader();
    for (final TableColumnHeader loColumnHeader : loRootHeader.getColumnHeaders())
    {
      try
      {
        final TableColumn<?, ?> loColumn = (TableColumn<?, ?>) loColumnHeader.getTableColumn();
        if (loColumn != null)
        {
          final Method loMethod = loSkin.getClass().getDeclaredMethod("resizeColumnToFitContent", TableColumn.class, int.class);
          loMethod.setAccessible(true);
          loMethod.invoke(loSkin, loColumn, 30);
        }
      }
      catch (final Throwable loErr)
      {
        loErr.printStackTrace(System.err);
      }
    }

  }

  // ---------------------------------------------------------------------------------------------------------------------
}
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
// ---------------------------------------------------------------------------------------------------------------------
Eddie Fann
  • 81
  • 3
0
    table.skinProperty().addListener((a, b, newSkin) -> {
        TableViewSkin<?> skin = (TableViewSkin<?>) table.getSkin();
        TableHeaderRow headerRow = skin.getTableHeaderRow();
        NestedTableColumnHeader rootHeader = headerRow.getRootHeader();
        for (TableColumnHeader columnHeader : rootHeader.getColumnHeaders()) {
            try {
                TableColumn<?, ?> column = (TableColumn<?, ?>) columnHeader.getTableColumn();
                if (column != null) {
                    Method method = skin.getClass().getDeclaredMethod("resizeColumnToFitContent", TableColumn.class, int.class);
                    method.setAccessible(true);
                    method.invoke(skin, column, 30);
                }
            } catch (Throwable e) {
                e = e.getCause();
                e.printStackTrace(System.err);
            }
        }

    });

In order to prevent getSkin() == null;

Guangheng Xu
  • 149
  • 1
  • 10