如何为 TableColumn 设置 CellValueFactory 以允许访问整个对象?

How to set CellValueFactory for TableColumn to allow access to entire object?

我有一个简单的 Person 对象列表,我正在使用 TableView 来显示:

class Person {

    private final StringProperty name = new SimpleStringProperty();
    private final StringProperty emailAddress = new SimpleStringProperty();
    private final StringProperty phoneNumber = new SimpleStringProperty();

    public Person(String name, String emailAddress, String phoneNumber) {

        this.name.set(name);
        this.emailAddress.set(emailAddress);
        this.phoneNumber.set(phoneNumber);

    }
}

我在这个例子中的目标是在同一个 TableColumn 中同时显示 emailAddressphoneNumber。这不仅仅是简单地连接值,因为我正在为此专栏实现自定义 TableCell(实际应用程序更复杂):

private TableCell<Person, Person> buildContactCell() {

    return new TableCell<Person, Person>() {

        final VBox root = new VBox();
        final Label lblEmailAddress = new Label();
        final Label lblPhoneNumber = new Label();

        {
            root.getChildren().addAll(lblEmailAddress, lblPhoneNumber);
        }

        @Override
        protected void updateItem(Person person, boolean empty) {

            super.updateItem(person, empty);
            if (person != null && !empty) {
                lblEmailAddress.setText(person.getEmailAddress());
                lblPhoneNumber.setText(person.getPhoneNumber());
                setGraphic(root);
            } else {
                setGraphic(null);
            }
        }
    };
}

我假设这工作正常,但我不确定如何为这个 TableColumn 配置 CellValueFactory 以接受整个 Person 对象。

是否有另一种方法可以设置 TableCell 以访问 Person 中的多个 属性?我在实现多个属性时看到的其他问题只涉及连接,这不是我要找的。


完整代码:

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TableCellSample extends Application {

    public static void main(String[] args) {

        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Simple Interface
        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        root.getChildren().add(getTableView());

        // Show the stage
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Sample");
        primaryStage.show();
    }

    private TableView<Person> getTableView() {

        TableView<Person> tableView = new TableView<>();

        TableColumn<Person, String> colName = new TableColumn<>("Name");
        colName.setCellValueFactory(cellData -> cellData.getValue().nameProperty());

        TableColumn<Person, Person> colContact = new TableColumn<>("Contact");
        colContact.setCellFactory(cell -> buildContactCell());

        // **********************************************************************************************
        // Need to set the CellValueFactory for colContact here
        // **********************************************************************************************

        tableView.getColumns().addAll(colName, colContact);
        tableView.setItems(getSampleData());
        return tableView;

    }

    private TableCell<Person, Person> buildContactCell() {

        return new TableCell<Person, Person>() {

            final VBox root = new VBox();
            final Label lblEmailAddress = new Label();
            final Label lblPhoneNumber = new Label();

            {
                root.getChildren().addAll(lblEmailAddress, lblPhoneNumber);
            }

            @Override
            protected void updateItem(Person person, boolean empty) {

                super.updateItem(person, empty);
                if (person != null && !empty) {
                    lblEmailAddress.setText(person.getEmailAddress());
                    lblPhoneNumber.setText(person.getPhoneNumber());
                    setGraphic(root);
                } else {
                    setGraphic(null);
                }
            }
        };
    }

    private ObservableList<Person> getSampleData() {

        ObservableList<Person> persons = FXCollections.observableArrayList();
        persons.addAll(
                new Person("Jack", "jack@outlook.com", "123-456-7890"),
                new Person("Jenny", "goodtime@yahoo.com", "555-867-5309"),
                new Person("Jesse", "mygirl@hotmail.com", "846-989-9988"));
        return persons;
    }
}

class Person {

    private final StringProperty name = new SimpleStringProperty();
    private final StringProperty emailAddress = new SimpleStringProperty();
    private final StringProperty phoneNumber = new SimpleStringProperty();

    public Person(String name, String emailAddress, String phoneNumber) {

        this.name.set(name);
        this.emailAddress.set(emailAddress);
        this.phoneNumber.set(phoneNumber);

    }

    public String getName() {

        return name.get();
    }

    public StringProperty nameProperty() {

        return name;
    }

    public String getEmailAddress() {

        return emailAddress.get();
    }

    public StringProperty emailAddressProperty() {

        return emailAddress;
    }

    public String getPhoneNumber() {

        return phoneNumber.get();
    }

    public StringProperty phoneNumberProperty() {

        return phoneNumber;
    }
}

您的单元格值工厂需要 return 一个包含行元素的 ObjectProperty<Person>。您可以在单元格值工厂中使用 cellData.getValue() 获取行数据,其中 cellData 是传递给 call() 方法的参数。所以:

colContact.setCellValueFactory(cellData -> 
    new SimpleObjectProperty<>(cellData.getValue()));

模型应该真正满足视图的需要,让 TableCell 在幕后窥视整个 TableModel 是一种代码味道。组织模型可能更好,这样数据就可以直接用于 TableCell。尝试组合,像这样:

public class TableCellSample extends Application {

    public static void main(String[] args) {

        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Simple Interface
        VBox root = new VBox(10);
        root.setAlignment(Pos.CENTER);
        root.setPadding(new Insets(10));

        root.getChildren().add(getTableView());

        // Show the stage
        primaryStage.setScene(new Scene(root));
        primaryStage.setTitle("Sample");
        primaryStage.show();
    }

    private TableView<Person> getTableView() {

        TableView<Person> tableView = new TableView<>();

        TableColumn<Person, String> colName = new TableColumn<>("Name");
        colName.setCellValueFactory(cellData -> cellData.getValue().nameProperty());

        TableColumn<Person, ContactInfo> colContact = new TableColumn<>("Contact");
        colContact.setCellFactory(cell -> buildContactCell());
        colContact.setCellValueFactory(cellData -> cellData.getValue().contactInfoProperty());

        // **********************************************************************************************
        // Need to set the CellValueFactory for colContact here
        // **********************************************************************************************

        tableView.getColumns().addAll(colName, colContact);
        tableView.setItems(getSampleData());
        return tableView;

    }

    private TableCell<Person, ContactInfo> buildContactCell() {

        return new TableCell<Person, ContactInfo>() {

            final VBox root = new VBox();
            final Label lblEmailAddress = new Label();
            final Label lblPhoneNumber = new Label();

            {
                root.getChildren().addAll(lblEmailAddress, lblPhoneNumber);
            }

            @Override
            protected void updateItem(ContactInfo contactInfo, boolean empty) {

                super.updateItem(contactInfo, empty);
                if (contactInfo != null && !empty) {
                    lblEmailAddress.setText(contactInfo.getEmailAddress());
                    lblPhoneNumber.setText(contactInfo.getPhoneNumber());
                    setGraphic(root);
                } else {
                    setGraphic(null);
                }
            }
        };
    }

    private ObservableList<Person> getSampleData() {

        ObservableList<Person> persons = FXCollections.observableArrayList();
        persons.addAll(
                new Person("Jack", "jack@outlook.com", "123-456-7890"),
                new Person("Jenny", "goodtime@yahoo.com", "555-867-5309"),
                new Person("Jesse", "mygirl@hotmail.com", "846-989-9988"));
        return persons;
    }
}

class Person {
    private final StringProperty name = new SimpleStringProperty();
    private final ObjectProperty<ContactInfo> contactInfoProperty = new SimpleObjectProperty<>();

    public Person(String name, String emailAddress, String phoneNumber) {
        this.name.set(name);
        contactInfoProperty.set(new ContactInfo(emailAddress, phoneNumber));
    }

    public String getName() {
        return name.get();
    }

    public StringProperty nameProperty() {
        return name;
    }

    public String getEmailAddress() {
        return contactInfoProperty.get().emailAddressProperty().get();
    }

    public StringProperty emailAddressProperty() {
        return contactInfoProperty.get().emailAddressProperty();
    }

    public String getPhoneNumber() {
        return contactInfoProperty.get().getPhoneNumber();
    }

    public StringProperty phoneNumberProperty() {
        return contactInfoProperty.get().phoneNumberProperty();
    }

    public ObjectProperty<ContactInfo> contactInfoProperty() {
        return contactInfoProperty;
    }
}

class ContactInfo {
    private final StringProperty emailAddress = new SimpleStringProperty();
    private final StringProperty phoneNumber = new SimpleStringProperty();

    public ContactInfo(String emailAddress, String phoneNumber) {
        this.emailAddress.set(emailAddress);
        this.phoneNumber.set(phoneNumber);
    }

    public String getEmailAddress() {
        return emailAddress.get();
    }

    public StringProperty emailAddressProperty() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress.set(emailAddress);
    }

    public String getPhoneNumber() {
        return phoneNumber.get();
    }

    public StringProperty phoneNumberProperty() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber.set(phoneNumber);
    }
}