繁体   English   中英

JavaFX-在 TableView 中居中选定的行

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM