【问题标题】:position of TableColumn in SceneTableColumn 在场景中的位置
【发布时间】:2015-06-12 12:12:03
【问题描述】:

我有一个包含多个 TableColumn 的 TableView,我想在某个 TableColumn 下方放置一个节点。如何获取 TableColumn 的确切位置(x,y 坐标),以便绑定节点的 translate 属性?

这是我如何在 TableView 的右上角放置一个按钮的 sn-p:

button.translateXProperty().unbind();
button.translateXProperty().bind(tableView.widthProperty().divide(2.0).subtract(button.getWidth() / 2.0 + 2.0) + tableView.localToScene(0.0, 0.0).getX());

这很好用,但显然只适用于 TableView。 TableColumns 没有那些 translate 属性或 localToScene 方法,所以我无法直接获取我想要绑定节点的位置。

我目前的解决方案(效果不太好)是执行以下操作: 我读出了我的 TableView 在 Scene (PointA) 中的位置,然后遍历所有列的列表 (tableView.getColumns()) 并检查它们中的每一个是否可见,如果是,将它们的宽度添加到 X- A 点的值。我这样做,直到找到我想要将节点放在下面的实际列。 现在的问题是,我不能真正将节点位置绑定到这一点,因为当我更改列的顺序或使其中一个不可见时,我的列会改变屏幕上的位置。我必须为列顺序和可见性添加一个侦听器...

有没有更有效的方法来做我想做的事? :D

【问题讨论】:

    标签: javafx javafx-8


    【解决方案1】:

    我通常不喜欢使用查找,但您可以使用查找 .table-view .column-header .label 检索用于显示列标题的标签,然后使用该标签的边界绑定按钮的布局属性。

    例子:

    import java.util.Optional;
    import java.util.function.Function;
    
    import javafx.application.Application;
    import javafx.beans.binding.Bindings;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.beans.value.ObservableValue;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.scene.control.Button;
    import javafx.scene.control.Label;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableView;
    import javafx.scene.layout.Pane;
    import javafx.stage.Stage;
    
    public class TableColumnLocationExample extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            TableView<Person> table = new TableView<>();
            table.getColumns().add(column("First Name", Person::firstNameProperty, 120));
            table.getColumns().add(column("Last Name", Person::lastNameProperty, 120));
            table.getColumns().add(column("Email", Person::emailProperty, 250));
    
            table.getItems().addAll(
                    new Person("Jacob", "Smith", "jacob.smith@example.com"),
                    new Person("Isabella", "Johnson", "isabella.johnson@example.com"),
                    new Person("Ethan", "Williams", "ethan.williams@example.com"),
                    new Person("Emma", "Jones", "emma.jones@example.com"),
                    new Person("Michael", "Brown", "michael.brown@example.com")        
            );
    
            Pane root = new Pane(table);
            Scene scene = new Scene(root, 600, 600);
            primaryStage.setScene(scene);
            primaryStage.show();
    
    
            for (TableColumn<Person, ?> col : table.getColumns()) {
                Optional<Label> header = findLabelForTableColumnHeader(col.getText(), root);
                header.ifPresent(label ->  {
                    Button button = new Button(col.getText());
    
                    button.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> 
                        label.getBoundsInLocal().getWidth(), label.boundsInLocalProperty()));
                    button.minWidthProperty().bind(button.prefWidthProperty());
                    button.maxWidthProperty().bind(button.prefWidthProperty());
    
                    button.layoutXProperty().bind(Bindings.createDoubleBinding(() -> 
                        label.getLocalToSceneTransform().transform(label.getBoundsInLocal()).getMinX(),
                        label.boundsInLocalProperty(), label.localToSceneTransformProperty()));
    
                    button.layoutYProperty().bind(Bindings.createDoubleBinding(() ->
                        table.getBoundsInParent().getMaxY() ,table.boundsInParentProperty()));
    
                    root.getChildren().add(button);
    
                });
            }
    
    
        }
    
        private Optional<Label> findLabelForTableColumnHeader(String text, Parent root) {
            return root.lookupAll(".table-view .column-header .label")
                    .stream()
                    .map(Label.class::cast)
                    .filter(label -> label.getText().equals(text))
                    .findAny(); // assumes all columns have unique text...
        }
    
    
    
        private <S,T> TableColumn<S,T> column(String title, Function<S,ObservableValue<T>> property, double width) {
            TableColumn<S,T> col = new TableColumn<>(title);
            col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
            col.setPrefWidth(width);
            return col ;
        }
    
        public static class Person {
            private StringProperty firstName = new SimpleStringProperty();
            private StringProperty lastName = new SimpleStringProperty();
            private StringProperty email = new SimpleStringProperty();
    
            public Person(String firstName, String lastName, String email) {
                setFirstName(firstName);
                setLastName(lastName);
                setEmail(email);
            }
    
            public final StringProperty firstNameProperty() {
                return this.firstName;
            }
    
            public final String getFirstName() {
                return this.firstNameProperty().get();
            }
    
            public final void setFirstName(final String firstName) {
                this.firstNameProperty().set(firstName);
            }
    
            public final StringProperty lastNameProperty() {
                return this.lastName;
            }
    
            public final String getLastName() {
                return this.lastNameProperty().get();
            }
    
            public final void setLastName(final String lastName) {
                this.lastNameProperty().set(lastName);
            }
    
            public final StringProperty emailProperty() {
                return this.email;
            }
    
            public final String getEmail() {
                return this.emailProperty().get();
            }
    
            public final void setEmail(final String email) {
                this.emailProperty().set(email);
            }
    
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    【讨论】:

    • 啊,我想这会起作用,而且比我的“解决方法”要好一点:D
    • 您可能希望将宽度绑定到标签的父级 - 否则如果显示排序图标,它会稍微偏离。
    【解决方案2】:

    如果你被允许使用非公共 api,你可以考虑通过它的皮肤访问 TableColumnHeader,只要它是 TableViewSkinBase 类型:它有 api 来访问 TableRowHeader,它是所有 TableColumnHeaders 的容器,并且有 api 来查找它包含的任何列的标题。

    代码 sn-p(宽度/位置绑定是从 James 的示例中复制的,只是复制到标题而不是标签)

    private void buttonsPerHeader(TableView<Person> table, Pane root) {
        if (!(table.getSkin() instanceof TableViewSkinBase)) return;
        TableViewSkinBase skin = (TableViewSkinBase) table.getSkin();
        TableHeaderRow headerRow = skin.getTableHeaderRow();
        for (TableColumn col : table.getColumns()) {
            TableColumnHeader header = headerRow.getColumnHeaderFor(col);
            Button button = new Button(col.getText());
    
            button.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> 
                header.getBoundsInLocal().getWidth(), header.boundsInLocalProperty()));
            button.minWidthProperty().bind(button.prefWidthProperty());
            button.maxWidthProperty().bind(button.prefWidthProperty());
    
            button.layoutXProperty().bind(Bindings.createDoubleBinding(() -> 
                header.getLocalToSceneTransform().transform(header.getBoundsInLocal()).getMinX(),
                header.boundsInLocalProperty(), header.localToSceneTransformProperty()));
    
            button.layoutYProperty().bind(Bindings.createDoubleBinding(() ->
                table.getBoundsInParent().getMaxY() ,table.boundsInParentProperty()));
    
            root.getChildren().add(button);
        }
    
    }
    

    【讨论】:

    • 我将尝试这个解决方案,因为它不依赖于查找或可选项。我现在会选择这个作为最佳答案,但我仍然希望有一种更简洁的方法来做到这一点,也许是在 JavaFX 的更新版本中:D
    • @user2336377 我们可能在 jdk9 中很幸运:由于模块化,皮肤 api 正在调查中以公开......或者完全丢失:由于模块化,内部 api 将无法以任何方式访问(将 James 的查找作为唯一选项)
    • 我刚刚在我的程序中实现了它,但它不能按原样工作,因为在尝试将标签绑定到列位置时出现以下错误:“Label.layoutX : 绑定值不能放。”但是当我将绑定从 '.layoutXProperty().bind(...)' 更改为 '.translateXProperty().bind(...)' 时,一切都按我想要的方式工作:)
    • 指着 James 获取绑定代码 - 感谢您的提醒,不过,不太明白为什么它会在您的上下文中发生,但很高兴您找到了解决方案 :-)
    猜你喜欢
    • 2014-09-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-09-30
    • 1970-01-01
    相关资源
    最近更新 更多