jbr*_*rnd 7 java controller javafx fxml
我制作了一个控制器和视图(fxml)的结构,以尽可能多地分离我的代码,我想知道如何在两个控制器之间进行通信.我的意思是,控制器必须调用另一个控制器的某些功能来将其设置为最新.
我认为我当前结构的模式将更加明确:
控制器1
/ \
fx:包括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
{
}
Run Code Online (Sandbox Code Playgroud)
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="view" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="pappu.controllers.FolderController">
<TabPane>
<tabs>
<Tab text="RECHERCHE">
<content>
<AnchorPane id="Content">
<children>
<fx:include source="FolderList.fxml" />
</children>
</AnchorPane>
</content>
</Tab>
<Tab text="DOSSIER">
<content>
<AnchorPane id="Content">
<children>
<fx:include source="FolderFormAdd.fxml" />
</children>
</AnchorPane>
</content>
</Tab>
</tabs>
</TabPane>
</VBox>
Run Code Online (Sandbox Code Playgroud)
控制器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<Folder> foldersTableView;
/**
* FolderNumber column object
*/
@FXML private TableColumn<Folder, String> colFolderNumber;
/**
* Person column object
*/
@FXML private TableColumn<Folder, String> colPerson;
/**
* Birthday date column object
*/
@FXML private TableColumn<Folder, Date> colBirthdayDate;
/**
* List of folders
*/
private static List<Folder> 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<Folder,String>("folderNumber")
);
colPerson.setCellValueFactory(
new Callback<CellDataFeatures<Folder, String>, ObservableValue<String>>() {
public ObservableValue<String> call(CellDataFeatures<Folder, String> p) {
return new SimpleStringProperty(p.getValue().getFirstName() + " " + p.getValue().getLastName());
}}
);
colBirthdayDate.setCellValueFactory(
new PropertyValueFactory<Folder,Date>("birthdayDate")
);
}
/**
* Put the folders list in the TableView object
*/
public void loadData()
{
ObservableList<Folder> listFold = FXCollections.observableArrayList(foldersList);
foldersTableView.setItems(listFold);
}
}
Run Code Online (Sandbox Code Playgroud)
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.Label?>
<VBox fx:id="view" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="pappu.controllers.FolderListController">
<Label fx:id="lblTest"></Label>
<TableView fx:id="foldersTableView">
<columns>
<TableColumn prefWidth="75.0" text="N°" fx:id="colFolderNumber">
</TableColumn>
<TableColumn prefWidth="75.0" text="Personne" fx:id="colPerson">
</TableColumn>
<TableColumn prefWidth="75.0" text="Date de naissance" fx:id="colBirthdayDate">
</TableColumn>
</columns>
</TableView>
</VBox>
Run Code Online (Sandbox Code Playgroud)
控制器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();
}
}
Run Code Online (Sandbox Code Playgroud)
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<VBox fx:id="view" prefHeight="216.0" prefWidth="421.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="pappu.controllers.FolderFormAddController">
<children>
<Label prefHeight="26.0" prefWidth="102.0" text="Numéro de dossier" />
<TextField prefWidth="200.0" fx:id="folderNumber"/>
<Label text="Prénom" />
<TextField prefWidth="200.0" fx:id="firstName"/>
<Label text="Nom" />
<TextField prefWidth="200.0" fx:id="lastName"/>
<Button mnemonicParsing="false" onAction="#submitForm" text="Enregistrer" />
</children>
</VBox>
Run Code Online (Sandbox Code Playgroud)
精确:
我在这个基础上创建了我的应用程序:http:
//www.zenjava.com/2011/10/25/views-within-views-controllers-within-controllers/我在Java JDK 7上使用JavaFX 2
我觉得JavaFX应用程序的全局功能缺少一些东西.
小智 9
Nikos提出了关于耦合的一个好点(软件工程原理).有一种方法可以实现第一种(简单)方法的"精神",而不是通过使用Mediator模式来蚕食这一原则.取自维基百科(引用GoF):
"Mediator模式的本质是"定义一个封装一组对象如何交互的对象."它通过保持对象明确地相互引用来促进松散耦合,并允许它们的交互独立变化.客户端类可以使用中介向其他客户端发送消息,并可以通过中介类上的事件从其他客户端接收消息."
在这里,您可以将控制器视为客户端.你需要做的就是使用调解员来调解彼此之间的"对话".
首先创建一个中介接口:
public interface IMediateControllers {
void registerController2(Controller2 controller);
void registerController3(Controller3 controller);
void controller2DoSomething();
void controller3OperateOn(String data);
}
Run Code Online (Sandbox Code Playgroud)
然后是一个具体的调解员(作为一个单身人士)
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();
}
}
Run Code Online (Sandbox Code Playgroud)
现在,由于Controller1注入了Controller2和Controller3(如fxml文件中所述),您可以在Controller1 :: initialize()方法中执行以下操作:
@Override
public void initialize(Url url, ResourceBundle resource) {
ControllerMediator.getInstance().registerController2(controller2Controller);
ControllerMediator.getInstance().registerController3(controller3Controller);
}
Run Code Online (Sandbox Code Playgroud)
现在,无论您需要Controller2与Controller3进行通信,您只需使用中介:
// ... somewhere in Controller2
ControllerMediator.getInstance().controller3OperateOn("my data");
Run Code Online (Sandbox Code Playgroud)
和控制器3可以使用相同的介体与Controller2通信:
// ... somewhere in Controller3
ControllerMediator.getInstance().controller2DoSomething();
Run Code Online (Sandbox Code Playgroud)
当然,这依赖于已实现doSomething()操作的Controller2和已实现该operateOn(String data)操作的Controller3 .
重要的是你已经将Controller2和Controller3分离(他们彼此不了解).我刚刚在一个我正在研究的小项目中使用了这个模式(受到Nikos的第一个解决方案的启发,但是立即考虑Mediator模式以消除他(正确)抓住的耦合.
我想到了两种方式:
基于"FXML简介"(链接)的"嵌套控制器"部分,您可以将子控制器(2和3)注入父(1)并让父级协调它们的交互:
FXML(1):
<fx:include source="FolderList.fxml" fx:id="list" />
...
<fx:include source="FolderFormAdd.fxml" fx:id="addForm" />
Run Code Online (Sandbox Code Playgroud)
Java(1)(注意字段的名称;必须匹配<fx:id>Controller,即):
public class FolderController extends AbstractController {
@FXML private FolderListController listController;
@FXML private FolderFormAddController addFormController;
void initialize() {
// add code to coordinate them
}
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,我不太喜欢这种解决方案,因为它会导致组件之间的强耦合.另一方面,它可能是最快的.
使用事件总线(例如来自Google Guava).这实际上可以解耦你的逻辑(例如,列表组件监听PersonAdded事件,无论它是如何创建的;表单生成此事件,而不关心谁在监听 - 如果有的话).我想在你的情况下我更喜欢这个解决方案.可以选择使用依赖注入来检索事件总线.
查看jewelsea评论中指出的答案,这很棒 - 我已经自己赞成了:)