两个JavaFx控制器之间的通信

我制作了一个控制器和视图(fxml)的结构,以尽可能多地分离我的代码,我想知道如何在2个控制器之间进行通信。 我的意思是,控制器必须调用另一个控制器的某些function来将其设置为最新。

我认为我当前结构的模式将更加明确:

控制器1
/ \
fx:include fx:include
/ \
Controller2 Controller3

每个控制器都有自己的fxml视图。
– 控制器1:容器控制器,其TabPane元素带有2个选项卡(每个选项卡对应1个控制器)
– 控制器2:列表
– 控制器3:表格

您可能已经猜到我希望我的表单(控制器3)自动更新我的列表(控制器2)。 目前,表单只是一个“创建表单”,所以我只想在列表中添加行。

我已经尝试使用FXMLoader获取我的Controller 2并调用函数来重新启动我的tableView,但没有成功。

控制器1(.java + .fxml):

package pappu.controllers; import pappu.core.controller.AbstractController; public class FolderController extends AbstractController { } 

                            

控制器2(.java + .fxml):

 package pappu.controllers; import java.net.URL; import java.util.Date; import java.util.List; import java.util.ResourceBundle; import org.hibernate.Session; import javafx.beans.property.SimpleStringProperty; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.TableColumn; import javafx.scene.control.TableColumn.CellDataFeatures; import javafx.scene.control.TableView; import javafx.scene.control.cell.PropertyValueFactory; import javafx.util.Callback; import pappu.core.controller.AbstractController; import pappu.entities.Folder; public class FolderListController extends AbstractController implements Initializable { /** * TableView object */ @FXML private TableView foldersTableView; /** * FolderNumber column object */ @FXML private TableColumn colFolderNumber; /** * Person column object */ @FXML private TableColumn colPerson; /** * Birthday date column object */ @FXML private TableColumn colBirthdayDate; /** * List of folders */ private static List foldersList; /** * Constructor * Will make a call to initializeFoldersList() */ public FolderListController() { initializeFoldersList(); } /** * Initialize implementation of the Initializable interface * * @param location * @param resources */ @Override public void initialize(URL location, ResourceBundle resources) { initializeTableColumns(); loadData(); } /** * Query the database to retrieve the folder list */ @SuppressWarnings("unchecked") public void initializeFoldersList() { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); foldersList = session.createQuery("from Folder").list(); session.close(); } /** * Initialize columns binding to folders properties */ public void initializeTableColumns() { colFolderNumber.setCellValueFactory( new PropertyValueFactory("folderNumber") ); colPerson.setCellValueFactory( new Callback<CellDataFeatures, ObservableValue>() { public ObservableValue call(CellDataFeatures p) { return new SimpleStringProperty(p.getValue().getFirstName() + " " + p.getValue().getLastName()); }} ); colBirthdayDate.setCellValueFactory( new PropertyValueFactory("birthdayDate") ); } /** * Put the folders list in the TableView object */ public void loadData() { ObservableList listFold = FXCollections.observableArrayList(foldersList); foldersTableView.setItems(listFold); } } 

                   

控制器3(.java + .fxml):

 package pappu.controllers; import java.io.IOException; import java.net.URL; import java.util.ResourceBundle; import org.hibernate.Session; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.fxml.Initializable; import javafx.scene.control.TextField; import javafx.scene.layout.Pane; import pappu.core.AppFactory; import pappu.core.controller.AbstractController; import pappu.entities.Folder; import pappu.entities.Gender; public class FolderFormAddController extends AbstractController { @FXML TextField folderNumber; @FXML TextField firstName; @FXML TextField lastName; public void submitForm() throws IOException { Session session = sessionFactory.getCurrentSession(); session.beginTransaction(); Folder folder = new Folder(); folder.setFolderNumber(folderNumber.getText()); folder.setFirstName(firstName.getText()); folder.setLastName(lastName.getText()); folder.setGender(Gender.m); session.save(folder); session.getTransaction().commit(); // This doesn't work.. even tried with a simple Label AppFactory app = new AppFactory(); FolderListController flc = app.folderListController(); flc.initializeFoldersList(); flc.loadData(); } } 

       

精度:
我在这个基础上创建了我的应用程序: http : //www.zenjava.com/2011/10/25/views-within-views-controllers-within-controllers/我在Java JDK 7上使用JavaFX 2

我觉得JavaFX应用程序的全局function缺少一些东西。

我想到了两种方式:

  1. 基于“FXML简介”( 链接 )的“嵌套控制器”部分,您可以将子控制器(2和3)注入父级(1)并让父级协调它们的交互:

    FXML(1):

      ...  

    Java(1)(注意字段的名称;必须匹配Controller ,即):

     public class FolderController extends AbstractController { @FXML private FolderListController listController; @FXML private FolderFormAddController addFormController; void initialize() { // add code to coordinate them } } 

    在这种情况下,我不太喜欢这种解决方案,因为它会导致组件之间的强耦合。 另一方面,它可能是最快的。

  2. 使用事件总线(例如来自Google Guava )。 这实际上可以解耦你的逻辑(例如,列表组件监听PersonAdded事件,无论它是如何创建的;表单生成此事件,而不关心谁在监听 – 如果有的话)。 我想在你的情况下我更喜欢这个解决方案。 可以选择使用dependency injection来检索事件总线。

看看jewelsea的评论所指出的答案,这很棒 – 我已经自己赞成了:)

Nikos提出了关于耦合的一个好点(软件工程原理)。 有一种方法可以实现第一种(简单)方法的“精神”,而不是通过使用Mediator模式来蚕食这一原则。 取自维基百科(引用GoF):

“Mediator模式的本质是”定义一个封装一组对象如何交互的对象。“它通过保持对象明确地相互引用来促进松耦合,并允许它们的交互独立变化。客户类可以使用中介向其他客户端发送消息,并可以通过中介类上的事件从其他客户端接收消息。“

在这里,您可以将控制器视为客户端。 那么你需要做的就是使用调解员来调解彼此之间的“对话”。

首先创建一个中介接口:

 public interface IMediateControllers { void registerController2(Controller2 controller); void registerController3(Controller3 controller); void controller2DoSomething(); void controller3OperateOn(String data); } 

然后是一个具体的调解员(作为一个单身人士)

 public class ControllerMediator implements IMediateControllers { private Controller2 controller2; private Controller3 controller3; @Override void registerController2(Controller2 controller) { controller2 = controller; } @Override void registerController3(Controller3 controller) { controller3 = controller; } @Override void controller2DoSomething() { controller2.doSomething(); } void controller3OperateOn(String data) { controller3.operateOn(data); } /** * Everything below here is in support of Singleton pattern */ private ControllerMediator() {} public static ControllerMediator getInstance() { return ControllerMediatorHolder.INSTANCE; } private static class ControllerMediatorHolder { private static final ControllerMediator INSTANCE = new ControllerMediator(); } } 

现在,由于Controller1注入了Controller2和Controller3(如fxml文件中所述),您可以在Controller1 :: initialize()方法中执行以下操作:

 @Override public void initialize(Url url, ResourceBundle resource) { ControllerMediator.getInstance().registerController2(controller2Controller); ControllerMediator.getInstance().registerController3(controller3Controller); } 

现在,无论您需要Controller2与Controller3进行通信,您只需使用中介:

 // ... somewhere in Controller2 ControllerMediator.getInstance().controller3OperateOn("my data"); 

和控制器3可以使用相同的调解器与Controller2通信:

 // ... somewhere in Controller3 ControllerMediator.getInstance().controller2DoSomething(); 

当然,这依赖于已实现doSomething()操作的Controller2和已实现了operateOn(String data)操作的Controller3。

重要的是你已经解耦了Controller2和Controller3(他们彼此不了解)。 我刚刚在一个我正在研究的小项目中使用了这个模式(受到Nikos的第一个解决方案的启发,但是立即考虑Mediator模式以消除他(正确)抓住的耦合。

我为程序员找到了一个易于实现的解决方案,他们对如何将fx:controller =“Controller”从他们的FXML文件传递到主类和/或控制器感到困惑。 允许这些类之间的对象引用。

从Main.java – > start方法:(使用fx:controller =“Controller”)在FXML文件中

  FXMLLoader loader = new FXMLLoader(); Parent root = loader.load(getClass().getResource("Window1.fxml").openStream()); Controller controller = loader.getController(); controller.setReferenceToController(controller); 

最后一行[controller.setReferenceToController(controller);]将从FXML文件加载的对象“Controller”传递给自身。

在Controller类中:

  private Controller controller; private DataProcess data; public void setReferenceToController(Controller controller){ this.controller = controller; data = new DataProcess(controller); } 

现在每次打开或启动一个新的“Window”或创建一个单独的类(如DataProcess)时,只需在它们之间传递控制器对象引用即可。 这将允许控制器,FXML和类之间的完全通信。

方法示例:

  public DataProcess(Controller controller) { this.controller = controller; } //Call this method from Controller public void handleSaveClick(){ if(file != null){ //save a bunch of data controller.setSaveStatus(true); } else controller.setSaveStatus(false); 

希望这可以帮助。