JavaFX 一对多(1 :n) In TableView

标签 java javafx


我是 JavaFX 新手,想要创建一个 TableView。
我选择了以下包含 ArrayList 的 Person 类示例。
OneToMany-relation(1:n) 应该像这样显示。

╔════╤════════════════════╤════════════════════╗
║    │        name        │         car        ║
║ id ├──────────┬─────────┼──────────┬─────────╢
║    │  forname │ surname │   brand  │  model  ║
╠════╪══════════╪═════════╪══════════╪═════════╣
║  0 │ John     │ Doe     │ Audi     │ A3      ║
║    │          │         │ VW       │ Golf3   ║
╟────┼──────────┼─────────┼──────────┼─────────╢
║  1 │ Jane     │ Doe     │ BMW      │ i5      ║
╚════╧══════════╧═════════╧══════════╧═════════╝

人员类别:

class Person
{
    private SimpleStringProperty forname;
    private SimpleStringProperty surname;
    private List<Car> cars;

    public Person(String forname, String surname, List<Car> cars)
    {
        this.forname.set(forname);
        this.surname.set(surname);
        this.lehrveranstaltungen = lehrveranstaltungen;

    }
    public Person()
    {
        this("","",new ArrayList<Car>());
    }

    public String getForname()
    {
        return forname.get();
    }
    public String getSurname()
    {
        return surname.get();
    }
    public List<Car> getCars()
    {
        return cars;
    }
}

汽车类别看起来与此类似

最简单的方法是什么?

我遇到的主要问题 是为每个汽车对象创建一个子行。
我该如何为 carColumn 设计 CellFactory ?
我尝试了这个例子JavaFX populate tableview with a OneToMany relationship 但是如何用汽车类的属性填充汽车的子列呢?它仅适用于 1 个属性。我是否必须向每个子列声明汽车的属性?

最佳答案

最简单的方法是将“品牌”列和“型号”列的数据值设置为汽车列表;然后使用单元工厂将单元格的文本设置为每辆车的适当值,并用换行符分隔。

这有一些缺点:不能绝对保证具有多行文本的列表单元格中所有行的高度相等,因此不能完全保证这些行完全对齐。我认为对于这个用例来说它可以正常工作。如果您想要更多控制,您可以创建 VBox您将其用作单元格的图形,并用单独的标签填充它,每辆车一次。这样您就可以根据需要控制标签的高度;您可能可以设置标签样式以提供边框效果,因此它看起来确实像“子单元格”。

此外,如果在显示表格时更改数据,这些单元格将不会更新,尽管这更多是由于您使用普通 List<Car> 造成的。对于汽车。这可以通过使用适当的 ObservableList 来修复s,如果需要的话也许可以使用提取器。

最后,这仅使用单个单元格来呈现汽车品牌和汽车型号的每个列表,因此您无法选择单个品牌或型号。

简单的解决方案如下所示:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class TableViewWithList extends Application {

    // Cell implementation used later to create cells with multiline text
    // from a cell item that is a List
    public static class MultilineCell<S, T, U extends List<T>> extends TableCell<S, U> {

        // function mapping each element of the list to a string
        private final Function<T, String> textMapper ;

        public MultilineCell(Function<T,String> textMapper) {
            this.textMapper = textMapper ;
        }

        @Override
        protected void updateItem(U items, boolean empty) {
            super.updateItem(items, empty);
            if (empty) {
                setText(null);
            } else {

                // map each element of the list to a string, and join them with
                // a new line between each
                setText(
                    items.stream()
                        .map(textMapper)
                        .collect(Collectors.joining("\n"))
                );
            }
        }
    }


    // Utility method to create a table column with a given title and function mapping
    // the row value to a property to be used as the data for cells in that column
    private <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  ;
    }

    @Override
    public void start(Stage primaryStage) {

        TableColumn<Person, List<Car>> brandCol = column("Brand", 
                person -> new SimpleObjectProperty<>(person.getCars()));

        TableColumn<Person, List<Car>> modelCol = column("Model",
                person -> new SimpleObjectProperty<>(person.getCars()));

        brandCol.setCellFactory(tc -> new MultilineCell<>(Car::getBrand));
        modelCol.setCellFactory(tc -> new MultilineCell<>(Car::getModel));        

        TableView<Person> table = new TableView<>();
        TableColumn<Person, Void> nameCol = new TableColumn<>("Name");
        TableColumn<Person, Void> carCol = new TableColumn<>("Car");
        table.getColumns().add(nameCol);
        table.getColumns().add(carCol);

        nameCol.getColumns().add(column("First Name", Person::firstNameProperty));
        nameCol.getColumns().add(column("Last Name", Person::lastNameProperty));

        carCol.getColumns().add(brandCol);
        carCol.getColumns().add(modelCol);


        Car a3 = new Car("Audi", "A3");
        Car golf = new Car("VW", "Golf GTI");
        Car i5 = new Car("BMW", "i5");

        table.getItems().addAll(
                new Person("John", "Doe", a3, golf),
                new Person("Jane", "Doe", i5)
        );

        Scene scene = new Scene(table);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    public static class Car {

        private final String brand ;
        private final String model ;

        public Car(String brand, String model) {
            this.brand = brand;
            this.model = model;
        }

        public String getBrand() {
            return brand;
        }

        public String getModel() {
            return model;
        }
    }

    public static class Person {

        private final StringProperty firstName = new SimpleStringProperty();
        private final StringProperty lastName = new SimpleStringProperty();

        private final List<Car> cars ;

        public Person(String firstName, String lastName, Car...cars) {
            setFirstName(firstName);
            setLastName(lastName);
            this.cars = new ArrayList<>(Arrays.asList(cars));
        }

        public List<Car> getCars() {
            return cars ;
        }

        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);
    }
}

呈现为

enter image description here

关于JavaFX 一对多(1 :n) In TableView,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48927203/

相关文章:

java - JFrame嵌套布局线程错误中异常

java - 使用 jersey 客户端通过 HTTPS 调用 api 时出现错误 handshake_failure。我已经关闭了服务器证书有效性检查

java - 在限制 Java/Android 范围内设置随机按钮位置

java - JPA/Hibernate:在复杂类上使用AttributeConverter时创建UserType是否明显?

java - JavaFX-插入从音频文件获取的媒体标签后,ArrayLists打印为空

javaFX-如何在屏幕底部放置文本区域

java - 保持 Gridpane 相同的高度和宽度(纵横比)

java - 无法在 Eclipse 中找到 SDK 文件夹

java - 这是最小化绑定(bind)失效的有效方法吗?

java - 从网格 Pane 中删除行并将剩余行向上移动