Created
November 2, 2017 20:56
-
-
Save hohonuuli/29dba349e6cf7d55bdb9d17e97392720 to your computer and use it in GitHub Desktop.
Solution to stack overflow question at https://stackoverflow.com/questions/46474385/how-to-find-the-indices-of-the-visible-rows-in-a-tableview-in-javafx-9
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.LinkedHashSet; | |
import javafx.collections.ObservableList; | |
import javafx.scene.control.TableRow; | |
import javafx.scene.control.TableView; | |
import javafx.util.Callback; | |
/** | |
* Extra calls for TableView which would have been nice to see in JavaFx TableView | |
*/ | |
public class TableViewExt<T> {// com.sun.javafx: implements ChangeListener<Skin<?>> | |
private final TableView<T> tableView; | |
private LinkedHashSet<TableRow<T>> rows = new LinkedHashSet<>(); | |
private int firstIndex; | |
private int lastIndex; | |
public TableViewExt(TableView<T> tableView) { | |
this.tableView = tableView; | |
// Callback to monitor row creation and to identify visible screen rows | |
final Callback<TableView<T>, TableRow<T>> rf = tableView.getRowFactory(); | |
final Callback<TableView<T>, TableRow<T>> modifiedRowFactory = param -> { | |
TableRow<T> r = rf != null ? rf.call(param) : new TableRow<T>(); | |
// Save row, this implementation relies on JaxaFX re-using TableRow efficiently | |
rows.add(r); | |
return r; | |
}; | |
tableView.setRowFactory(modifiedRowFactory); | |
} | |
/** | |
* Changes the current view to ensure that one of the passed index positions | |
* is visible on screen. The view is not changed if any of the passed index positions is already visible. | |
* The table scroll position is moved so that the closest index to the current position is visible. | |
* @param indices Assumed to be in ascending order. | |
* | |
*/ | |
public void | |
scrollToIndex(int ... indices) { | |
int first = getFirstVisibleIndex(); | |
int last = getLastVisibleIndex(); | |
int where = first; | |
boolean changeScrollPos = true; | |
// No point moving current scroll position if one of the index items is visible already: | |
if (first >= 0 && last >= first) { | |
for (int idx : indices) { | |
if (first <= idx && idx <= last) { | |
changeScrollPos = false; | |
break; | |
} | |
} | |
} | |
if (indices.length > 0 && changeScrollPos) { | |
where = indices[0]; | |
if (first >= 0) { | |
int x = closestTo(indices, first); | |
int abs = Math.abs(x - first); | |
if (abs < Math.abs(where - first)) | |
{ | |
where = x; | |
} | |
} | |
if (last >= 0) { | |
int x = closestTo(indices, last); | |
int abs = Math.abs(x - last); | |
if (abs < Math.abs(where - last)) | |
{ | |
where = x; | |
} | |
} | |
tableView.scrollTo(where); | |
} | |
} | |
private static int closestTo(int[] indices, int value) { | |
int x = indices[0]; | |
int diff = Math.abs(value - x); | |
int newDiff = diff; | |
for (int v : indices) { | |
newDiff = Math.abs(value - v); | |
if (newDiff < diff) { | |
x = v; | |
diff = newDiff; | |
} | |
} | |
return x; | |
} | |
private void recomputeVisibleIndexes() { | |
firstIndex = -1; | |
lastIndex = -1; | |
// Work out which of the rows are visible | |
double tblViewHeight = tableView.getHeight(); | |
double headerHeight = tableView.lookup(".column-header-background").getBoundsInLocal().getHeight(); | |
double viewPortHeight = tblViewHeight - headerHeight; | |
for(TableRow<T> r : rows) { | |
double minY = r.getBoundsInParent().getMinY(); | |
double maxY = r.getBoundsInParent().getMaxY(); | |
boolean hidden = (maxY < 0) || (minY > viewPortHeight); | |
// boolean fullyVisible = !hidden && (maxY <= viewPortHeight) && (minY >= 0); | |
if (!hidden) { | |
if (firstIndex < 0 || r.getIndex() < firstIndex) { | |
firstIndex = r.getIndex(); | |
} | |
if (lastIndex < 0 || r.getIndex() > lastIndex) { | |
lastIndex = r.getIndex(); | |
} | |
} | |
} | |
} | |
/** | |
* Find the first row in the tableView which is visible on the display | |
* @return -1 if none visible or the index of the first visible row (wholly or fully) | |
*/ | |
public int getFirstVisibleIndex() { | |
recomputeVisibleIndexes(); | |
//System.out.println("getFirstVisibleIndex "+firstIndex+" rows="+rows.size()/*com.sun.javafx:+" jre8Index="+jre8Index*/); | |
return firstIndex; | |
} | |
/** | |
* Find the last row in the tableView which is visible on the display | |
* @return -1 if none visible or the index of the last visible row (wholly or fully) | |
*/ | |
public int getLastVisibleIndex() { | |
recomputeVisibleIndexes(); | |
//System.out.println("getLastVisibleIndex "+lastIndex+" rows="+rows.size()/*com.sun.javafx:+" jre8Index="+jre8Index*/); | |
return lastIndex; | |
} | |
/** | |
* Ensure that some part of the current selection is visible in the display view | |
*/ | |
public void scrollToSelection() { | |
ObservableList<Integer> seln = tableView.getSelectionModel().getSelectedIndices(); | |
int[] indices = new int[seln.size()]; | |
for (int i = 0; i < indices.length; i++) { | |
indices[i] = seln.get(i); | |
} | |
scrollToIndex(indices); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment