[英]JavaFX- center selected row in TableView
在 JavaFX 8 中,能够执行以下操作以始终在视口中间的 TableView 中将选定行居中:
TableView<T> tv = getTableView();
// Position selection in the middle of the viewPort.
if (tv.getSelectionModel().getSelectedItem() != null) {
TableViewSkin<?> ts = (TableViewSkin<?>) tv.getSkin();
Optional<VirtualFlow> vfOpt = ts.getChildren().stream()
.filter(child -> child instanceof VirtualFlow)
.map(VirtualFlow.class::cast)
.findFirst();
// TODO sometimes not centering correctly. The scrollTo used in JavaFX 17
// differs from that used in JavaFX 8!
if (vfOpt.isPresent()) {
VirtualFlow vf = vfOpt.get();
int first = vf.getFirstVisibleCell().getIndex();
int last = vf.getLastVisibleCell().getIndex();
int selectedIndex = tv.getSelectionModel().getSelectedIndex();
int scrollPosition = selectedIndex - ((last - first) / 2) > 0 ? selectedIndex - ((last - first) / 2) : 0;
vf.scrollTo(scrollPosition);
}
}
在 JavaFX 17 中,这不再有效。 我将其追溯到 vf.scrollTo(int) 方法的实现,与 JavaFX 8 相比,该方法发生了一些变化。上面的代码有时会起作用,有时会不起作用(取决于第一个和最后一个索引)。
我记下了以下内容(FI = first,LA = last,SEL = selectedIndex,POS = 计算滚动 position,RES 结果):
FI = 0, LA = 16, SEL = 13, POS = 5, RES = to top of viewport
FI = 12, LA = 29, SEL = 13, POS = 5, RES = to middle of viewport
FI = 5, LA = 21, SEL = 13, POS = 5, RES = to top of viewport
因此,它似乎与计算的 position 已经落在视口内有关,导致所选行 go 到顶部。
任何人都可以提供任何帮助吗?
VirtualFlow.scrollTo(int)
只保证指定索引处的项目在视口中可见; 它不保证它在视口中的位置。
相反,您可以使用VirtualFlow.scrollToTop(int)
将所选项目移动到视口的顶部(如果可能),然后使用Viewport.scrollPixels(double)
调整视口高度的一半。 您需要在两者之间布局视口(我认为否则第二个调用会覆盖第一个调用,尽管我并不完全清楚)。
这种方法应该比你原来的方法更健壮,因为它只依赖于规范,而不是实际的实现,尽管我没有在 JavaFX 18 之前的版本上进行测试。
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXMLLoader;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.skin.TableViewSkin;
import javafx.scene.control.skin.VirtualFlow;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import java.io.IOException;
public class HelloApplication extends Application {
@Override
public void start(Stage stage) throws IOException {
TableView<Item> table = new TableView<>();
for (int i = 1 ; i <= 1000 ; i++) {
table.getItems().add(new Item("Item "+i));
}
TableColumn<Item, String> col = new TableColumn<>("Item");
col.setCellValueFactory(data -> data.getValue().nameProperty());
table.getColumns().add(col);
Button scrollSelected = new Button("Scroll to Selected");
scrollSelected.setOnAction(e -> {
int selected = table.getSelectionModel().getSelectedIndex();
if (selected == -1) return ;
TableViewSkin<?> skin = (TableViewSkin<?>) table.getSkin();
skin.getChildren().stream()
.filter(VirtualFlow.class::isInstance)
.map(VirtualFlow.class::cast)
.findAny()
.ifPresent(vf -> {
vf.scrollToTop(selected);
vf.layout();
vf.scrollPixels(-vf.getHeight()/2);
});
});
HBox controls = new HBox(scrollSelected);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(5));
BorderPane root = new BorderPane(table);
root.setBottom(controls);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static class Item {
private final StringProperty name = new SimpleStringProperty();
public Item(String name) {
this.name.set(name);
}
public StringProperty nameProperty() {
return name ;
}
}
public static void main(String[] args) {
launch();
}
}
还有各种其他解决方案,例如
vf.scrollTo(selected);
vf.layout();
Cell<?> cell = vf.getCell(selected);
double y = cell.getBoundsInParent().getCenterY();
vf.scrollPixels(y - vf.getHeight()/2);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.