当前位置: 首页 > 知识库问答 >
问题:

JavaFx-字符串和流窗格(行?)在tableView中?

汝楷
2023-03-14

我目前正在尝试实现以下内容:

>

  • 以ObservableList作为数据集的TableView,有两列,每列都包含字符串(播放器的名称)。这部分很简单。

    单击播放器(名称)后,应在选定播放器下方注入自定义FlowPane。如果单击另一个播放器,则流窗格应消失并注入当前单击的播放器下方。

    下面的代码实现了TableView(减去鼠标侦听器部分)。请帮我让FlowPane跨越整行。我猜我需要一个RowFactory,但不知道如何让它为我的目的工作:)

    另外,显然我的两列现在显示相同的数据。令人困惑的是:)有没有办法告诉一列使用一半的数据集,另一列使用另一半?我显然不希望我的数据显示两次。

    public class main extends Application
    {
        public static void main(String[] args)
        {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) throws Exception
        {
            try
            {
    
                FlowPane f = new FlowPane();
                Scene scene = new Scene(f, 300, 200);
    
                Player p1 = new Player("player 1 ");
                Player p2 = new Player("player 2 ");
                Player p3 = new Player("player 3 ");
    
                ArrayList<Object> players = new ArrayList<>();
                players.add(p1);
                players.add(p2);
                players.add(p3);
    
                ObservableList<Object> observableList = FXCollections.observableArrayList(players);
                TableView<Object> table = createTableView(observableList, 300, 200);
    
                f.getChildren().add(table);
                injectFlowPane(table);
    
                stage.setScene(scene);
                stage.show();
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
    
        }
    
        public TableView<Object> createTableView(ObservableList<Object> items, double width, double height)
        {
            TableView<Object> table = new TableView<>();
    
            table.setItems(items);
    
            table.getColumns().add(createTableColumn(width / 2));
            table.getColumns().add(createTableColumn(width / 2));
    
    
            table.setMinSize(width, height);
            table.setPrefSize(width, height);
            table.setMaxSize(width, height);
            return table;
        }
    
        private TableColumn<Object, Object> createTableColumn(double width)
        {
            TableColumn<Object, Object> tableColumn = new TableColumn<>();
            tableColumn.setCellFactory(
                    new Callback<TableColumn<Object, Object>, TableCell<Object, Object>>() {
                        @Override
                        public TableCell<Object, Object> call(TableColumn<Object, Object> arg0)
                        {
                            return new PlayerCell();
                        }
                    });
    
            tableColumn.setCellValueFactory(cellDataFeatures -> {
    
                Object item = cellDataFeatures.getValue();
                return new SimpleObjectProperty<>(item);
    
            });
    
            tableColumn.setMinWidth(width);
    
            return tableColumn;
        }
    
        private void injectFlowPane(TableView<Object> table)
        {
            FlowPane f = new FlowPane();
            f.setMinSize(50, 50);
            f.setBackground(new Background(new BackgroundFill(Color.DARKGREEN, CornerRadii.EMPTY, Insets.EMPTY)));
            table.getItems().add(1, f);
        }
    
    }
    
    public class PlayerCell extends TableCell<Object, Object>
    {
        @Override
        protected void updateItem(Object item, boolean empty)
        {
            super.updateItem(item, false);
    
            //      if (empty)
    
            if (item != null)
            {
    
                if (item instanceof Player)
                {
                    setText(((Player) item).getName());
                    setGraphic(null);
                }
                else if (item instanceof FlowPane)
                {
                    setGraphic((FlowPane) item);
                }
                else
                {
                    setText("N/A");
                    setGraphic(null);
                }
            }
            else
            {
                setText(null);
                setGraphic(null);
            }
    
        }
    }
    
    
    public class Player
    {
        private String name;
    
        public Player(String name)
        {
            this.name = name;
        }
    
        public String getName()
        {
            return name;
        }
    
    }
    

    编辑:

    我现在已经实现了James\u D的ExpandingTableRow,它可以灵活地显示所选TableRow下面的流程窗格。我还设法改变了我的数据结构,使每一列现在显示不同的玩家,而不是每一列中的相同玩家。

    但是,创建的FlowPane实际上应该取决于在行中单击的实际玩家(单元格)。在James的示例中:如果选择了FirstName或LastName,则会创建不同的FlowPane(即使对于同一行)。FlowPane应该以相同的方式显示——在选定行的下方——但它是一个不同的新FlowPane,具体取决于是否单击了FirstName,或者是否单击了LastName。我如何才能做到这一点?

    我已经研究了如何使用:

    table.getSelectionModel().setCellSelectionEnabled(true);
    

    但这实际上似乎禁用了James\u d的解决方案。

  • 共有1个答案

    公孙智
    2023-03-14

    此解决方案仅适用于Java9及更高版本。

    行的显示由TableRow管理,该行的实际布局由其皮肤(aTableRowSkin)执行。因此,要管理它,您需要TableRow的子类来安装自定义皮肤。

    行实现非常简单:在此示例中,我添加了一个属性,用于在选择行时显示“附加内容”。它还覆盖了createDefaultSkin()方法以指定自定义皮肤实现。

    import javafx.beans.property.ObjectProperty;
    import javafx.beans.property.SimpleObjectProperty;
    import javafx.scene.Node;
    import javafx.scene.control.Skin;
    import javafx.scene.control.TableRow;
    
    public class ExpandingTableRow<T> extends TableRow<T> {
    
        private final ObjectProperty<Node> selectedRowContent = new SimpleObjectProperty<>();
    
        public final ObjectProperty<Node> selectedRowContentProperty() {
            return this.selectedRowContent;
        }
    
    
        public final Node getSelectedRowContent() {
            return this.selectedRowContentProperty().get();
        }
    
    
        public final void setSelectedRowContent(final Node selectedRowContent) {
            this.selectedRowContentProperty().set(selectedRowContent);
        }
    
        public ExpandingTableRow(Node selectedRowContent) {
            super();
            setSelectedRowContent(selectedRowContent);
        }
    
        public ExpandingTableRow() {
            this(null);
        }
    
        @Override
        protected Skin<?> createDefaultSkin() {
            return new ExpandingTableRowSkin<T>(this);
        }
    
    }
    

    皮肤实现必须完成布局工作。它需要重写计算高度的方法,如果需要,考虑额外内容的高度;如果需要,它需要重写layoutChildren()方法来定位额外内容。最后,它必须管理附加内容,如果行的选定状态更改(或附加内容本身更改),则添加或删除附加内容。

    import javafx.scene.control.skin.TableRowSkin;
    
    public class ExpandingTableRowSkin<T> extends TableRowSkin<T> {
    
        private ExpandingTableRow<T> row;
    
        public ExpandingTableRowSkin(ExpandingTableRow<T> row) {
            super(row);
            this.row = row;
    
            row.selectedRowContentProperty().addListener((obs, oldContent, newContent) -> {
                if (oldContent != null) {
                    getChildren().remove(oldContent);
                }
                if (newContent != null && row.isSelected()) {
                    getChildren().add(newContent);
                }
                if (row.getTableView() != null) {
                    row.getTableView().requestLayout();
                }
            });
            row.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
                if (isNowSelected && row.getSelectedRowContent() != null
                        && !getChildren().contains(row.getSelectedRowContent())) {
                    getChildren().add(row.getSelectedRowContent());
                } else {
                    getChildren().remove(row.getSelectedRowContent());
                }
            });
        }
    
        @Override
        protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset,
                double leftInset) {
            if (row.isSelected() && row.getSelectedRowContent() != null) {
                return super.computeMaxHeight(width, topInset, rightInset, bottomInset, leftInset)
                        + row.getSelectedRowContent().maxHeight(width);
            }
            return super.computeMaxHeight(width, topInset, rightInset, bottomInset, leftInset);
        }
    
        @Override
        protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset,
                double leftInset) {
            if (row.isSelected() && row.getSelectedRowContent() != null) {
                return super.computeMinHeight(width, topInset, rightInset, bottomInset, leftInset)
                        + row.getSelectedRowContent().minHeight(width);
            }
            return super.computeMinHeight(width, topInset, rightInset, bottomInset, leftInset);
        }
    
        @Override
        protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset,
                double leftInset) {
            if (row.isSelected() && row.getSelectedRowContent() != null) {
                return super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset)
                        + row.getSelectedRowContent().prefHeight(width);
            }
            return super.computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);
        }
    
        @Override
        protected void layoutChildren(double x, double y, double w, double h) {
            if (row.isSelected()) {
                double rowHeight = super.computePrefHeight(w, snappedTopInset(), snappedRightInset(), snappedBottomInset(),
                        snappedLeftInset());
                super.layoutChildren(x, y, w, rowHeight);
                row.getSelectedRowContent().resizeRelocate(x, y + rowHeight, w, h - rowHeight);
            } else {
                super.layoutChildren(x, y, w, h);
            }
        }
    
    }
    

    最后是一个测试(使用Oracle的常见示例或其版本):

    import java.util.function.Function;
    
    import javafx.application.Application;
    import javafx.beans.property.SimpleStringProperty;
    import javafx.beans.property.StringProperty;
    import javafx.beans.value.ObservableValue;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.TableColumn;
    import javafx.scene.control.TableRow;
    import javafx.scene.control.TableView;
    import javafx.scene.layout.FlowPane;
    import javafx.stage.Stage;
    
    public class ExpandingTableRowTest extends Application {
    
        @Override
        public void start(Stage primaryStage) {
            TableView<Person> table = new TableView<>();
            table.getColumns().add(column("First Name", Person::firstNameProperty));
            table.getColumns().add(column("Last Name", Person::lastNameProperty));
    
            table.setRowFactory(tv -> {
                Label label = new Label();
                FlowPane flowPane = new FlowPane(label);
                TableRow<Person> row = new ExpandingTableRow<>(flowPane) {
                    @Override
                    protected void updateItem(Person person, boolean empty) {
                        super.updateItem(person, empty);
                        if (empty) {
                            label.setText(null);
                        } else {
                            label.setText(String.format("Some additional information about %s %s here",
                                    person.getFirstName(), person.getLastName()));
                        }
                    }
                };
                return row;
            });
    
            table.getItems().addAll(
                    new Person("Jacob", "Smith"), 
                    new Person("Isabella", "Johnson"),
                    new Person("Ethan", "Williams"), 
                    new Person("Emma", "Jones"),
                    new Person("Michael", "Brown")
            );
    
            Scene scene = new Scene(table);
            primaryStage.setScene(scene);
            primaryStage.show();
        }
    
        private static <S, T> TableColumn<S, T> column(String title, Function<S, ObservableValue<T>> property) {
            TableColumn<S, T> col = new TableColumn<>(title);
            col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
            return col;
        }
    
        public static class Person {
            private final StringProperty firstName = new SimpleStringProperty();
            private final StringProperty lastName = new SimpleStringProperty();
    
            public Person(String firstName, String lastName) {
                setFirstName(firstName);
                setLastName(lastName);
            }
    
            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 static void main(String[] args) {
            launch(args);
        }
    }
    

    如您所见,可能需要对样式和大小进行一些细化才能使其准备好生产,但这显示了可行的方法。

     类似资料:
    • 我当前的代码如下所示: 注意:STable是一个包含每个列的所有setter和getter的类,我还设置了CellFactory。 任何关于如何做到这一点的指导都将是伟大的!

    • 问题内容: 我想填充与 我只有两列。我做不到 到目前为止,我尝试过的是: 桌上什么也没显示。但是当我进行打印时,我看到的是我添加的数据。但是在桌子上什么也没显示。 我创建列如下: 我在Internet上看到了一些示例,这些示例中创建了一个对象来填充对象,但我不想仅为表视图创建类。还有其他可行的方法吗? 编辑: 我只想拥有这样的东西: 问题答案: 首先,您必须在FXML文件或代码中创建table’c

    • 我有一个问题,我不能从一个对象加载3个属性显示在表视图上。我有一个付款对象,它由一个受益人对象(包括3个字符串-name、accountnumber和code)、一个金额字符串和一个引用字符串组成。如果这些项目都是同一个对象的一部分,我可以查看它们,但是当我将它们拆分时,它不会引起问题,并且单元格是空的。 每次我尝试将付款加载到tableView时,只有金额和引用是可见的。我尝试使用Propert

    • 我刚启动JavaFx,有点拘泥于TableView,它显示了每个列的非常长的字符串表示,如: 而我预计细胞中只会出现“大”、“5000”、“3000”。 这是我的模型: fxml: 最后是控制器: 看起来控制器很好,它能够从数据库中获取值并向TableView添加行,但为什么TableView显示属性对象的字符串表示,而不仅仅是显示值? 非常感谢!

    • getter称为getShowName()、getEpsWatched()和getShowStatus(),它们获得变量showName、epsWatched和showStatus。 这里怎么了?显示字符串,但不显示int。

    • 我是JavaFX的新手,我正在尝试将旧的Swing应用程序重构为JavaFX应用程序。 我还使用JPA(带有EclipseLink实现)来保存/检索数据。 所以我定义了一些带注释的POJO。 下面是我的注释POJO: TableViewControl有些问题: null