如何在JavaFX 9中查找TableView中可见行的索引

如何在JavaFX 9中获取TableView中可见行的索引? 在JavaFX 8中,我可以执行以下操作:

// --- The offending imports in Java 9 // import com.sun.javafx.scene.control.skin.TableViewSkin; // import com.sun.javafx.scene.control.skin.VirtualFlow; /** * This is a total hack. We need it as scrollTo jumps the selected * row to the top of the table. Jarring if the row is already * visible. As a workaround, we only scroll if the row isn't already * visible * * @return A 2 element ray with the start and end index of visible rows */ public int[] getVisibleRows() { TableView tableView = getTableView(); TableViewSkin skin = (TableViewSkin) tableView.getSkin(); if (skin == null) return new int[] {0, 0}; VirtualFlow flow = (VirtualFlow) skin.getChildren().get(1); int idxFirst; int idxLast; if (flow != null && flow.getFirstVisibleCellWithinViewPort() != null && flow.getLastVisibleCellWithinViewPort() != null) { idxFirst = flow.getFirstVisibleCellWithinViewPort().getIndex(); if (idxFirst > tableView.getItems().size()) { idxFirst = tableView.getItems().size() - 1; } idxLast = flow.getLastVisibleCellWithinViewPort().getIndex(); if (idxLast > tableView.getItems().size()) { idxLast = tableView.getItems().size() - 1; } } else { idxFirst = 0; idxLast = 0; } return new int[]{idxFirst, idxLast}; } 

在Java 9中,作为模块化的一部分,JDK团队隐藏了不应该公开的API(例如,所有以’com.sun’开头的包)如果我尝试在Java 9中编译它,我得到错误:

  [...] cannot find symbol [ERROR] symbol: class TableViewSkin [ERROR] location: package com.sun.javafx.scene.control.skin [...] cannot find symbol [ERROR] symbol: class VirtualFlow [ERROR] location: package com.sun.javafx.scene.control.skin 

有没有官方的方法来获取TableView中的可见行? 关于另一个的任何想法,更好的解决方法吗?

更新:基于@ user4712734解决方案的解决方案

 TableViewExt tableViewExt = new TableViewExt(tableView); public int[] getVisibleRows() { return new int[]{ tableViewExt.getFirstVisibleIndex(), tableViewExt.getLastVisibleIndex()}; } 

TableViewExt的代码

我遇到了同样的问题,因为我也在使用JavaFX 8特定的TableViewSkin / VirtualFlow代码,但是现在使用这个解决方法,其中包含了关于此问题的另一篇文章中的一些建议 – 请参阅Tableview可见行

此解决方法在Java8 / 9中运行和编译,并且不使用参考包com.sun.javafx。 要使用该类,您需要在构造TableView和向其添加行之间创建TableViewExtra:

 TableView tableView = new TableView(); TableViewExtra tvX = new TableViewExtra(tableView); // Add some rows etc 

然后你可以打电话:

 int firstVisRowIndex = tvX.getFirstVisibleIndex(); int lastVisRowIndex = tvX.getLastVisibleIndex(); txV.scrollToSelection(); 

您可以看到我曾在评论中使用的旧com.sun.javafx依赖项:

 import java.util.LinkedHashSet; //com.sun.javafx: START //import com.sun.javafx.scene.control.skin.TableViewSkin; //import com.sun.javafx.scene.control.skin.VirtualFlow; //import javafx.beans.value.ChangeListener; //import javafx.beans.value.ObservableValue; //import javafx.scene.Node; //import javafx.scene.control.IndexedCell; //import javafx.scene.control.Skin; // com.sun.javafx: END 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 TableViewExtra // com.sun.javafx: implements ChangeListener> { private final TableView tableView; // com.sun.javafx: private VirtualFlow flow; LinkedHashSet> rows = new LinkedHashSet<>(); private int firstIndex; private int lastIndex; public TableViewExtra(TableView tableView) { this.tableView = tableView; // com.sun.javafx: tableView.skinProperty().addListener(this); // Callback to monitor row creation and to identify visible screen rows final Callback, TableRow> rf = tableView.getRowFactory(); final Callback, TableRow> modifiedRowFactory = new Callback, TableRow>() { @Override public TableRow call(TableView param) { TableRow r = rf != null ? rf.call(param) : new TableRow(); // Save row, this implementation relies on JaxaFX re-using TableRow efficiently rows.add(r); return r; } }; tableView.setRowFactory(modifiedRowFactory); } // com.sun.javafx BEGIN // public void changed(ObservableValue> ov, Skin t, Skin t1) // { // if (t1 == null) { // return; // } // // TableViewSkin tvs = (TableViewSkin) t1; // ObservableList kids = tvs.getChildren(); // // if (kids == null || kids.isEmpty() || kids.size() < 2) { // return; // } // flow = (VirtualFlow) kids.get(1); // } // com.sun.javafx END /** * 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 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() { // com.sun.javafx: START // int jre8Index = -1; // if (flow != null) // { // IndexedCell cell = flow.getFirstVisibleCell(); // if (cell != null) // { // jre8Index = cell.getIndex(); // } // } // com.sun.javafx: END 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() { // com.sun.javafx: END // int jre8Index = -1; // if (flow != null) // { // IndexedCell cell = flow.getLastVisibleCell(); // if (cell != null) // jre8Index = cell.getIndex(); // } // com.sun.javafx: END 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 seln = tableView.getSelectionModel().getSelectedIndices(); int[] indices = new int[seln.size()]; for (int i = 0; i < indices.length; i++) { indices[i] = seln.get(i).intValue(); } scrollToIndex(indices); } } 

您可能会迁移到使用自Java9以来引入的模块javafx.controls: –

 module your.module { requires javafx.controls; } 

它根据代码中使用的类的要求导出javafx.scene.controljavafx.scene.control.skin包。

 module javafx.controls { ... exports javafx.scene.control.skin; exports javafx.scene.control; ... }