Reputation: 51
I want to implement a custom scroll functionality for a tableview. Therefore access to the visible rows and cells is needed.
How to detect the visible cells and rows in a JavaFX tableview? I don't have found anything about that in the API description for tableview.
Thanks
Upvotes: 4
Views: 5615
Reputation: 1378
I know this is an old question... anyway, should someone need it:
You can achieve it without VirtualFlow (which is a private API as "purring pigeon" mentions). Just use your own RowFactory and store the references to the TableRow objects as they're being created (some breakpoints or System.out.println() calls here can help quickly understand, how TableView works).
In your controller class, create these two fields:
private ArrayList<TableRow<Person>> tblViewPersonsTableRows = new ArrayList<>();
private boolean headerTableRowCreated = false;
... in the TableRowFactory, store the references...
final TableRow<Person> tableRow = new TableRow<Person>() {
...
}
if (!headerTableRowCreated) {
headerTableRowCreated = true; //the first TableRow corresponds to TableView's header so ignore it
} else {
int myIdx = tblViewPersonsTableRows.size();
tblViewPersonsTableRows.add(tableRow);
}
... now call this where you need it ...
double tblViewHeight = fxTblViewPersons.getHeight();
double headerHeight = fxTblViewPersons.lookup(".column-header-background").getBoundsInLocal().getHeight();
double viewPortHeight = tblViewHeight - headerHeight;
for (TableRow tableRow : tblViewPersonsTableRows) {
double minY = tableRow.getBoundsInParent().getMinY();
double maxY = tableRow.getBoundsInParent().getMaxY();
if ((maxY < 0) || (minY > viewPortHeight)) {
//row invisible
} else if ((maxY <= viewPortHeight) && (minY >= 0)) {
tableRow.getStyleClass().add("fullyVisibleRow");
} else {
tableRow.getStyleClass().add("partiallyVisibleRow");
}
}
... !!! BE CAREFUL !!! if you're calling it in some event handler, where the data or the sort order changes, check whether you get the old coordinates before the data changes or the new ones after the change.
If you use css styles like this...
.fullyVisibleRow {
-fx-control-inner-background: palegreen;
-fx-accent: derive(-fx-control-inner-background, -40%);
-fx-cell-hover-color: derive(-fx-control-inner-background, -20%);
}
.partiallyVisibleRow {
-fx-control-inner-background: orange;
-fx-accent: derive(-fx-control-inner-background, -40%);
-fx-cell-hover-color: derive(-fx-control-inner-background, -20%);
}
... the fully visible rows should be green and the partially visible ones (at the top and bottom of the TableView) orange. I hope this helps someone.
Upvotes: 2
Reputation: 309
Try this in a TableView to scroll to a wanted row. I've tried with table.scrollTo(index) but it doesn't scroll properly as it sets the wanted row as the first visible.
I've written the next code on my controller.
Here you can also see how to get the visible rows.
/**
* Used to manually scroll the Table
*/
private TableViewSkin<?> tableSkin;
private VirtualFlow<?> virtualFlow;
@Override
public void initialize(URL location, ResourceBundle resources) {
...
...
Platform.runLater( ()-> { loadVirtualFlow(); });
}
/**
* Loads the actual VirtualFlow
*/
private void loadVirtualFlow(){
tableSkin = (TableViewSkin<?>) tableContent.getSkin();
virtualFlow = (VirtualFlow<?>) tableSkin.getChildren().get(1);
}
/**
* Scrolls the table until the given index is visible
* @param index to be shown
*/
private void scrollTo(int index){
int first = virtualFlow.getFirstVisibleCell().getIndex();
int last = virtualFlow.getLastVisibleCell().getIndex();
if (index <= first){
while (index <= first && virtualFlow.adjustPixels(-1) < 0){
first = virtualFlow.getFirstVisibleCell().getIndex();
}
} else {
while (index >= last && virtualFlow.adjustPixels(1) > 0){
last = virtualFlow.getLastVisibleCell().getIndex();
}
}
}
With this code you can easily scroll to a desired index:
scrollTo(index);
After adding elements to the list, remember to call loadVirtualFlow before calling scrollTo, so the VirtualFlow gets updated and doesn't throw an exception.
Upvotes: 7