Ivanko
Ivanko

Reputation: 130

Keeping selected row in the middle of a TablView control

I would like to keep selected row in the middle of the TableView control regardless of how selection is changing. I have to buttons (PREV, NEXT) above TableView that I use to move change selected rows. The problem is that TableView scrollbar does not follow this selection so I need to call

TableView.scrollTo(int)

to keep selected row visible. However after calling scrollTo(int) selected row ends up on the top of the viewport. And I would like to have it in the middle of the viewport.

This was possible in Swing as shown here: http://www.java2s.com/Code/Java/Swing-JFC/ScrollingaCelltotheCenterofaJTableComponent.htm

Unfortunately, I am unable to solve this issue with JavaFX 8.

Upvotes: 3

Views: 1492

Answers (3)

Leonis
Leonis

Reputation: 296

An example of calling the show() method as suggested earlier by @Jakub in Java:

TableViewSkin<?> ts = (TableViewSkin<?>) tableView.getSkin();
VirtualFlow<?> vf = (VirtualFlow<?>) ts.getChildren().get(1);
vf.show(tableView.getSelectionModel().getSelectedIndex())

I also want to draw your attention to the fact that after this method it is not recommended to immediately call the refresh() method of the tableView.

Upvotes: 0

Jakub
Jakub

Reputation: 165

Just found slightly better solution to almost the same problem, unfortunately still private API, but works with variable row heights and scrolls only when necessary. I believe it's the same code that's called when UP/DOWN arrows are pressed. Instead of manually calculating the scroll position, just call VirtualFlow's .show(rowIndex).

Following code works under JVM1.8, it's Kotlin, but differs only in syntax from Java:

    val skin: Skin<*> = tableView.skin
    if (skin is SkinBase<*>) {
        skin.children
            .first { it is VirtualFlow<*> }
            .let { it as VirtualFlow<*> }
            .show(rowIndex)
    }

Keep in mind that VirtualFlow comes to public API in Java 9, so that will require a bit of rework (or maybe just changes in imports).

Upvotes: 1

Ivanko
Ivanko

Reputation: 130

Well this seems to work. It uses non-public API so I am still interested if someone has better solution.

    table.getSelectionModel().selectedIndexProperty().addListener((observable, oldValue, newValue) -> Platform.runLater(() -> {
        TableViewSkin<?> ts = (TableViewSkin<?>) table.getSkin();
        VirtualFlow<?> vf = (VirtualFlow<?>)ts.getChildren().get(1);

        int first = vf.getFirstVisibleCellWithinViewPort().getIndex();
        int last = vf.getLastVisibleCellWithinViewPort().getIndex();

        if((newValue.intValue() - ((last - first) / 2)) >= 0) {
            vf.scrollTo(newValue.intValue() - ((last - first) / 2));
        }
    }));

The limitation is that all rows must have same height.

Upvotes: 5

Related Questions