Javafx与Spring的ApplicationEventPublisher

时间:2017-01-05 09:47:57

标签: spring events javafx-8

我有一个JavaFX应用程序,它使用Spring Framework进行依赖注入。一切都很好。

唯一的问题是当我使用ApplicationEventPublisher发布其他控制器要监听的事件时。

方案

在添加新客户时,将捕获客户详细信息的模式发布者AddCustomerEvent,其中CustomersController侦听事件并获取新添加的客户并将其添加到可用的客户列表中。

问题

EventListener方法获取事件,从而获得新客户,但未添加到客户列表中。此外,当我硬编码'要添加到绑定到tableView的客户列表的新CustomerCustomer不会显示在tableView中。

出版商: 以下是发布者控制器。它发布一个事件,其中包含要订阅的其他控制器的新Customer

@Component
@Scope("prototype")
public class AddUpdateCustomerController implements Initializable {

    @FXML
    private Button btnSave;
    @FXML
    private Button btnCancel;
    @FXML
    private TextField txtFirstName;
    @FXML
    private TextField txtSecondName;
    @FXML
    private TextField txtPhone;

    @Autowired
    private ApplicationEventPublisher applicationEventPublisher;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        btnSave.setOnAction(this::handleSaveCustomer);
    }    

    private void handleSaveCustomer(ActionEvent event){
        String firstName = txtFirstName.getText();
        String secondName = txtSecondName.getText();
        String phone = txtPhone.getText();

        Customer c = new Customer(1, firstName, secondName, phone);
        AddCustomerEvent addCustomerEvent = new AddCustomerEvent(c);
        applicationEventPublisher.publishEvent(addCustomerEvent);
    }
}

订户: 此bean会听取发布者(AddUpdateCustomerController类)在方法addCustomerEventListener(AddCustomerEvent addCustomerEvent)

中发布的事件
@Component
@Scope("prototype")
public class CustomersController implements Initializable {

    @FXML
    private TableView<Customer> tblCustomers;
    @FXML
    private TableColumn<Customer, String> clmFirstName;
    @FXML
    private TableColumn<Customer, String> clmSecondName;
    @FXML
    private TableColumn<Customer, String> clmPhone;
    @FXML
    private Button addCustomer;

    private final ObservableList<Customer> customerList = FXCollections.observableArrayList();
    private final ListProperty<Customer> customerListProperty = new SimpleListProperty<>();

    @Autowired
    protected AnnotationConfigApplicationContext applicationContext;
    @Autowired
    protected Stage primaryStage;

    /**
     * Initializes the controller class.
     */
    @Override
    public void initialize(URL url, ResourceBundle rb) {
        clmFirstName.setCellValueFactory(new PropertyValueFactory("firstName"));
        clmSecondName.setCellValueFactory(new PropertyValueFactory("secondName"));
        clmPhone.setCellValueFactory(new PropertyValueFactory("phone"));
        customerListProperty.set(customerList);
        tblCustomers.itemsProperty().bind(customerListProperty);
        addCustomer.setOnAction(this::addCustomerActionEvent);

        customerList.add(new Customer(1, "Name 1", "Second 1", "001"));
        customerList.add(new Customer(2, "Name 2", "Second 2", "002"));
        customerList.add(new Customer(1, "Name 3", "Second 3", "003"));
        customerList.add(new Customer(1, "Name 4", "Second 4", "004"));

    }    

    private void addCustomerActionEvent(ActionEvent event){
        try {
            Parent loader
                    = SpringFXMLLoader.create()
                            .applicationContext(applicationContext)
                            .location(getClass().getResource("/fxml/addUpdateCustomer.fxml"))
                            .load();
            Stage addUpdateCustomerModal = new Stage();
            addUpdateCustomerModal.initOwner(primaryStage);
            addUpdateCustomerModal.centerOnScreen();
            addUpdateCustomerModal.setScene(new Scene(loader));
            addUpdateCustomerModal.show();
        } catch (IOException ex) {
            Logger.getLogger(CustomersController.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @EventListener
    public void addCustomerEventListener(AddCustomerEvent addCustomerEvent){
        System.out.println("Heey");
        customerList.add(addCustomerEvent.getCustomer());
        customerList.add(new Customer(1, "Name 5", "Second 5", "005"));
    }
}

我还创建了一个小型的javafx应用来重现this github repo

中描述的问题

程序

  1. 克隆并运行应用
  2. 点击Add Customer按钮。
  3. 填写first namesecond namephone
  4. 点击Save按钮。
  5. 您会注意到刚添加的成员不在表格视图中

1 个答案:

答案 0 :(得分:1)

主要问题是我们使用控制器的不同实例,因为这是scope="prototype"。我们需要两件不同的东西:

    当FXML作为控制器加载时,注入
  • 1
  • 创建了另外一个用于侦听队列。

解决方案是创建一个正在侦听队列并存储客户列表的Singleton。然后通过@autowire在控制器中包含这个单例。

当然在这种情况下,总是不需要总线:你可以在AddCustomerController中添加这个单例,然后直接添加列表中的元素。

以下是单身人士的解决方案:

@Component
public class CustomersSingleton {

  private final ObservableList<Customer> customerList = FXCollections.observableArrayList();

  @EventListener
  public void addCustomerEventListener(AddCustomerEvent addCustomerEvent){
      System.out.println("Heey");
      customerList.add(addCustomerEvent.getCustomer());
      customerList.add(new Customer(1, "Name 5", "Second 5", "005"));
  }

  public ObservableList<Customer> getCustomerList() {
    return customerList;
  }

}

然后修改你的控制器

// remove attributes customerList and customerListProperty then modify method:

/**
 * Initializes the controller class.
 */
@SuppressWarnings ({ "rawtypes", "unchecked" })
@Override
public void initialize(URL url, ResourceBundle rb) {
    clmFirstName.setCellValueFactory(new PropertyValueFactory("firstName"));
    clmSecondName.setCellValueFactory(new PropertyValueFactory("secondName"));
    clmPhone.setCellValueFactory(new PropertyValueFactory("phone"));
    ObservableList<Customer> customerList = customersSingleton.getCustomerList();
    tblCustomers.setItems(customerList);
    addCustomer.setOnAction(this::addCustomerActionEvent);

    customerList.add(new Customer(1, "Name 1", "Second 1", "001"));
    customerList.add(new Customer(2, "Name 2", "Second 2", "002"));
    customerList.add(new Customer(1, "Name 3", "Second 3", "003"));
    customerList.add(new Customer(1, "Name 4", "Second 4", "004"));

}

附注:使其工作的简单但真正令人沮丧的解决方案只是删除@Scope("prototype")注释,但是你不能将单例作为控制器,因为屏幕有很多实例,每个屏幕都需要它的控制器

@Component
//@Scope("prototype")
public class CustomersController implements Initializable {

评论不要使用它!