JavaFX 9/10 no longer possible to override TableView.resizeColumnToFitContent
Asked Answered
M

2

1

In JavaFX 8 it was possible to override the TableView.resizeColumnToFitContent function. This is vital for our purposes since it allows us to enhance the way column headers are laid out beyond the default implementation, as well as to tune the performance where the default suffers somewhat in larger tables.

One example of how the layout behaviour has been enhanced is in the context of nested column headers. By default a leaf column header's prefWidth is set according to either the max width of the rows of data in that column, or to the width of the leaf column's title text, whichever is greater. But if that leaf header has some parent column headers with wider title text, they'll be clipped and an ellipsis displayed. This is not desirable for us, so we've changed resizeColumnToFitContent so the prefWidth of a leaf column header also takes into account the prefWidth of its parent column headers.

As of Java 9 and the below commit, the resizeColumnToFitContent function has been moved to a static location, removing the ability to customise this behaviour:

http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/53bfdfed5bbf

About a week ago I wrote to the author responsible for this change, Jonathan Giles, but I guess he's a busy guy and this is becoming a blocking issue for us and our plans to migrate our product to Java 10, so I thought I'd also see if the community knows how I might solve this. So far, the only option seems to be the somewhat brute force approach outlined here, but maybe there's a better way?

Marlowe answered 29/5, 2018 at 12:1 Comment(6)
FWIW, Jonathan no longer works for Oracle. This method was never actually part of the public API, was it?Ellinger
James is right - I no longer work at Oracle or have responsibility for any code there :-) I would suggest you take the brute force approach - it is basically what the code you want to have public did anyway (from memory)Jedda
Why can’t this function be part of the protected API? TableView is a core control in JavaFX and should be as extensible as possible, without having to resort to brute force. Do you think it would be worth my time submitting a PR to OpenJDK making it so?Marlowe
Basically, inheritance breaks encapsulation to some extent, because to override a method you have to know something about that method's implementation. This means that if the API allows a method to be overridden, then it commits the API to that particular implementation for all time. When the skin classes were moved to public API in Java 9, some methods were made private (or moved to utility classes); presumably these are methods where the JavaFX team is not yet ready to commit to the particular implementation used. Read Josh Bloch's "Effective Java", if you haven't already.Ellinger
James is right (again) - the best approach to API design is slow and cautious. Making an API that is as 'extensible as possible' is a very hard thing to do, because it implies that we basically just make the whole implementation public, and this locks us in forever to not being able to change anything related to the code, as it means we might break someone. This is why there is the concept of API and implementation being separated in the first place - relying on implementation, as was done here, is not wise as there is no guarantee it is going to stay stable.Jedda
I think perhaps you're overly cautious with respect to API design. Quote from Oracle's own docs: "As a class evolves, its API inevitably changes: methods are renamed for consistency, new and better methods are added, and fields change. But such changes introduce a problem. You need to keep the old API around until developers make the transition to the new one, but you don't want them to continue programming to the old API. The ability to deprecate a class, method, or member field solves the problem."Marlowe
P
1

If you want to override this method, why you just don't implement your own resizeColumnToFitContent like is was in TableSkinUtils ?

Phyfe answered 29/5, 2018 at 12:35 Comment(2)
The method is called from a private method in one of the skin classes (TableColumnHeader, and perhaps others), so you can't override the method that calls it. Consequently, and presumably by design, there's no way to redefine this behavior. (You'd basically end up re-implementing almost all of the skin from scratch.)Ellinger
This is indeed the case @James_D, there are a number of related classes which would need to be re-implementedMarlowe
C
1

With Java 16 we can now get access to resizeColumnToFitContent in a new way

class FitWidthTableView<T> extends TableView<T> {
    
    public FitWidthTableView() {
        setSkin(new FitWidthTableViewSkin<>(this));
    }

    public void resizeColumnsToFitContent() {
        Skin<?> skin = getSkin();
        if (skin instanceof FitWidthTableViewSkin<?> ctvs) ctvs.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);
    }
}
Classified answered 14/11, 2021 at 10:2 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.