2015-07-12 89 views
0

对于DTO,我使用POJO。因此,为了进行双向绑定,我创建了适配器。我的意思是,这样的事情:用于TableView的JavaFX属性适配器

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使用适配器的原因是,否则我们将不得不在TableView中复制适配器的代码,如果我们将POJO用于​​DTO和TableView。

正如我理解的TableView中的每一行我们必须创建一个新的适配器实例,我不明白该怎么做。

回答

2

解决方案,无需适配器类

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

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

注意,在上面的例子中,在编辑器中的文本字段不能直接与POJO类使用绑定(因为它不公开任何的JavaFX

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

解决方案属性);如果你想这样做,你可以为此目的创建更多JavaBeanStringProperty,但那会最终重复代码。如果您希望能够做到这一点,那么使用适配器类可能会有所帮助。以下是使用此解决方案的代码的样子。请注意,现在的适配器类公开JavaFX的属性,所以表的单元格值工厂只需直接映射到这些属性:创造的JavaBeanStringProperty s的封装在一个地方(适配器类):

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

+0

谢谢您的回答。这就是我的意思。我们在适配器和ValueFactory中都创建ObjectProperty - >代码复制。所以我们可以在TableView中使用相同的适配器。 –

+0

我想我不明白你为什么需要你的适配器类。 JavaBeanPropertyAdapter已经完成了JavaBean属性和JavaFX Observable值之间的“适配”;你不需要另一级适配器。你能不能完全忽略这个类,就像我的回答(我的回答中没有代码重复,对吧?)。 –

+0

我的意思是,你可以在'TableView'中使用适配器类,但是你必须创建一个'TableView '而不是'TableView '并填充它。假设你在某个地方生成了“List ”列表,你将不得不创建一个“列表”,并确保它始终与原来的“列表”同步。 JavaBeanPropertyAdapter类的全部意义在于让您在控件中使用普通的JavaBean ... –

相关问题