TableView的JavaFX属性适配器

时间:2015-07-12 17:56:33

标签: java javafx pojo

对于DTO我使用POJO。所以为了进行双向绑定,我创建了适配器。我的意思是,类似的东西:

POJO:

public class Temp{
   private BigDecimal weight;
   private final PropertyChangeSupport propertyChangeSupport;
   public Temp() {
        this.propertyChangeSupport = new PropertyChangeSupport(this);
   }
   public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertyChangeSupport.addPropertyChangeListener(listener);
   }
   public BigDecimal getWeight() {
     return weight;
   }
   public void setWeight(BigDecimal weight) {
     BigDecimal pv = this.weight;
    this.weight = weight;
    propertyChangeSupport.firePropertyChange("weight", pv, weight);
   }
}

我有以下适配器:

public class TempAdapter {
    private ObjectProperty<BigDecimal> weightProperty;
    public TempAdapter(Temp temp) {
        try {
            weightProperty=new JavaBeanObjectPropertyBuilder<BigDecimal>().bean(temp).name("weight").build();
            weightProperty.addListener(new ChangeListener<BigDecimal>() {
                @Override
                public void changed(ObservableValue<? extends BigDecimal> ov, BigDecimal t, BigDecimal t1) {
                  ....
                }
            });
        } catch (NoSuchMethodException ex) {
            Logger.getLogger(this.getClass().getName()).log(Level.SEVERE, null, ex);
        }
    }
 public ObjectProperty<BigDecimal> getWeightProperty() {
    return weightProperty;
}

但是,我无法理解如何将此适配器与TableView一起使用。我想为TableView使用适配器的原因是,如果我们使用POJO for DTO和TableView,我们将不得不在TableView中复制适配器的代码。

据我了解TableView中的每一行,我们必须创建一个新的适配器实例,我无法理解如何操作。

1 个答案:

答案 0 :(得分:3)

没有适配器类的解决方案

首先请注意,您不一定需要适配器类;您只需创建需要它们的JavaBeanProperty实例:在本例中为表格的单元格值工厂。如果UI中只有一个(或两个)位置需要直接绑定到与POJO中属性相对应的JavaFX属性,那么这可能就是这样。

这是使用通常的Oracle Person表示例的此技术的完整示例。在此示例中,没有适配器类:该表只在单元格值工厂中创建JavaBeanStringProperty适配器。有一个编辑表单,它直接与POJO类交互。

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

import javafx.application.Application;
import javafx.beans.property.adapter.JavaBeanStringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
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.control.TextField;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class PojoTable extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<Person> table = new TableView<>();
        table.setEditable(true);

        TableColumn<Person, String> firstNameColumn = createColumn("First Name", "firstName");
        TableColumn<Person, String> lastNameColumn = createColumn("Last Name", "lastName");

        table.getColumns().add(firstNameColumn);
        table.getColumns().add(lastNameColumn);


        Button button = new Button("Show data");
        button.setOnAction(e -> {
            table.getItems().stream().map(person -> person.getFirstName() + " " + person.getLastName())
                .forEach(System.out::println);
            System.out.println();
        });

        Button edit = new Button("Edit");
        edit.disableProperty().bind(table.getSelectionModel().selectedItemProperty().isNull());
        edit.setOnAction(e -> edit(table.getSelectionModel().getSelectedItem(), primaryStage));

        table.getItems().addAll(
                new Person("Jacob", "Smith"),
                new Person("Isabella", "Johnson"),
                new Person("Ethan", "Williams"),
                new Person("Emma", "Jones"),
                new Person("Michael", "Brown")
        );

        HBox buttons = new HBox(10, button, edit);
        buttons.setAlignment(Pos.CENTER);

        BorderPane root = new BorderPane(table, null, null, buttons, null);
        BorderPane.setAlignment(buttons, Pos.CENTER);
        BorderPane.setMargin(buttons, new Insets(10));
        root.setPadding(new Insets(10));
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
    }

    private void edit(Person person, Stage primaryStage) {
        GridPane editPane = new GridPane();
        TextField firstNameField = new TextField(person.getFirstName());
        TextField lastNameField = new TextField(person.getLastName());
        Button okButton = new Button("OK");
        Button cancelButton = new Button("Cancel");
        HBox buttons = new HBox(10, okButton, cancelButton);

        editPane.addRow(0, new Label("First Name:"), firstNameField);
        editPane.addRow(1, new Label("Last Name:"), lastNameField);
        editPane.add(buttons, 0, 2, 2, 1);

        GridPane.setHalignment(buttons, HPos.CENTER);
        GridPane.setMargin(buttons, new Insets(10));

        editPane.setPadding(new Insets(10));

        Scene scene = new Scene(editPane);
        Stage stage = new Stage();
        stage.setScene(scene);

        stage.initOwner(primaryStage);
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.initStyle(StageStyle.UNDECORATED);

        cancelButton.setOnAction(e -> stage.hide());
        okButton.setOnAction(e -> {
            person.setFirstName(firstNameField.getText());
            person.setLastName(lastNameField.getText());
            stage.hide();
        });

        stage.show();
    }

    private TableColumn<Person, String> createColumn(String title, String property) {
        TableColumn<Person, String> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> {
            Person p = cellData.getValue();
            try {
                JavaBeanStringProperty prop = new JavaBeanStringPropertyBuilder()
                    .bean(p)
                    .name(property)
                    .build();
                return prop;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        col.setCellFactory(TextFieldTableCell.forTableColumn());
        return col ;
    }

    public static class Person {
        private String firstName ;
        private String lastName ;

        private PropertyChangeSupport support ;

        public Person(String firstName, String lastName) {
            this.firstName = firstName ;
            this.lastName = lastName ;

            support = new PropertyChangeSupport(this);
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            String previous = this.firstName ;
            this.firstName = firstName;
            support.firePropertyChange("firstName", previous, firstName);
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            String previous = this.lastName ;
            this.lastName = lastName;
            support.firePropertyChange("lastName", previous, lastName);
        }

        public void addPropertyChangeListener(PropertyChangeListener listener) {
            support.addPropertyChangeListener(listener);
        }

        public void removePropertyChangeListener(PropertyChangeListener listener) {
            support.removePropertyChangeListener(listener);
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}

使用适配器类的解决方案

请注意,在上面的示例中,编辑器中的文本字段不能直接与POJO类一起使用绑定(因为它不公开任何JavaFX属性);如果你想这样做,你可以为此目的创建更多的JavaBeanStringProperty,但最终会复制代码。如果您希望能够这样做,那么使用适配器类可能会有所帮助。以下是使用此解决方案的代码。请注意,现在适配器类公开了JavaFX属性,因此表的单元格值工厂可以直接映射到这些属性:JavaBeanStringProperty的创建被封装在一个地方(适配器类):

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;

import javafx.application.Application;
import javafx.beans.property.StringProperty;
import javafx.beans.property.adapter.JavaBeanStringPropertyBuilder;
import javafx.geometry.HPos;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
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.control.TextField;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.StageStyle;

public class PojoTable extends Application {

    @Override
    public void start(Stage primaryStage) {
        TableView<PersonAdapter> table = new TableView<>();
        table.setEditable(true);

        TableColumn<PersonAdapter, String> firstNameColumn = createColumn("First Name", PersonAdapter::firstNameProperty);
        TableColumn<PersonAdapter, String> lastNameColumn = createColumn("Last Name", PersonAdapter::lastNameProperty);

        table.getColumns().add(firstNameColumn);
        table.getColumns().add(lastNameColumn);

        List<Person> data = Arrays.asList(
            new Person("Jacob", "Smith"),
            new Person("Isabella", "Johnson"),
            new Person("Ethan", "Williams"),
            new Person("Emma", "Jones"),
            new Person("Michael", "Brown")
        );


        Button button = new Button("Show data");
        button.setOnAction(e -> {
            data.stream().map(person -> person.getFirstName() + " " + person.getLastName())
                .forEach(System.out::println);
            System.out.println();
        });

        Button edit = new Button("Edit");
        edit.disableProperty().bind(table.getSelectionModel().selectedItemProperty().isNull());
        edit.setOnAction(e -> edit(table.getSelectionModel().getSelectedItem(), primaryStage));

        data.stream().map(PersonAdapter::new).forEach(table.getItems()::add);

        HBox buttons = new HBox(10, button, edit);
        buttons.setAlignment(Pos.CENTER);

        BorderPane root = new BorderPane(table, null, null, buttons, null);
        BorderPane.setAlignment(buttons, Pos.CENTER);
        BorderPane.setMargin(buttons, new Insets(10));
        root.setPadding(new Insets(10));
        primaryStage.setScene(new Scene(root, 600, 600));
        primaryStage.show();
    }

    private void edit(PersonAdapter person, Stage primaryStage) {
        GridPane editPane = new GridPane();
        TextField firstNameField = new TextField();
        firstNameField.textProperty().bindBidirectional(person.firstNameProperty());

        TextField lastNameField = new TextField();
        lastNameField.textProperty().bindBidirectional(person.lastNameProperty());

        Button okButton = new Button("OK");
        HBox buttons = new HBox(10, okButton);

        editPane.addRow(0, new Label("First Name:"), firstNameField);
        editPane.addRow(1, new Label("Last Name:"), lastNameField);
        editPane.add(buttons, 0, 2, 2, 1);

        GridPane.setHalignment(buttons, HPos.CENTER);
        GridPane.setMargin(buttons, new Insets(10));

        editPane.setPadding(new Insets(10));

        Scene scene = new Scene(editPane);
        Stage stage = new Stage();
        stage.setScene(scene);

        stage.initOwner(primaryStage);
        stage.initModality(Modality.APPLICATION_MODAL);
        stage.initStyle(StageStyle.UNDECORATED);

        okButton.setOnAction(e -> {
            stage.hide();
        });

        stage.show();
    }

    private TableColumn<PersonAdapter, String> createColumn(String title, Function<PersonAdapter, StringProperty> property) {
        TableColumn<PersonAdapter, String> col = new TableColumn<>(title);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        col.setCellFactory(TextFieldTableCell.forTableColumn());
        return col ;
    }

    public static class Person {
        private String firstName ;
        private String lastName ;

        private PropertyChangeSupport support ;

        public Person(String firstName, String lastName) {
            this.firstName = firstName ;
            this.lastName = lastName ;

            support = new PropertyChangeSupport(this);
        }

        public String getFirstName() {
            return firstName;
        }

        public void setFirstName(String firstName) {
            String previous = this.firstName ;
            this.firstName = firstName;
            support.firePropertyChange("firstName", previous, firstName);
        }

        public String getLastName() {
            return lastName;
        }

        public void setLastName(String lastName) {
            String previous = this.lastName ;
            this.lastName = lastName;
            support.firePropertyChange("lastName", previous, lastName);
        }

        public void addPropertyChangeListener(PropertyChangeListener listener) {
            support.addPropertyChangeListener(listener);
        }

        public void removePropertyChangeListener(PropertyChangeListener listener) {
            support.removePropertyChangeListener(listener);
        }
    }

    public static class PersonAdapter {
        private final Person person ;

        private final StringProperty firstName ;
        private final StringProperty lastName ;

        public PersonAdapter(Person person) {
            this.person = person ;

            try {
                this.firstName = new JavaBeanStringPropertyBuilder()
                    .bean(person)
                    .name("firstName")
                    .build();

                this.lastName = new JavaBeanStringPropertyBuilder()
                    .bean(person)
                    .name("lastName")
                    .build();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }

        }

        public Person getPerson() { 
            return person ;
        }

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

此方法的一个可能的缺点是对基础列表(简单示例中的data)的更改不会传播到表中(这意味着添加或删除data中的元素不会更改该表;在表的现有元素上调用setFirstNamesetLastName将允许更新)。有关管理此问题的技巧,请参阅Best practice to decorate an ObservableList and retain change events