javafx在我们国内资料还是很少,国外的文档想我这种英语不精的实在没有办法看;
有这篇文章的原因是因为我在开发一款Mybatis帮助工具,需要实现多窗口以及数据交互,我给这款工具起名叫MyBatis-CMEU,是一款可以让人忘记怎么写mapper的工具,与该工具配套的Assist帮助类很简单却很强大;现在已经在开发2.0版,2.0版将会重构该工具变得完美;工具使用的连接--->点击打开链接
在进入主题先推荐一个javafx群:518914410,我也在这个群里面欢迎进来跟大家交流;
友情提示:中心想法将需要操作的舞台与控制器的引用保存到map中,使用完毕后将这个map里面的引用删除,不排除java不释放内存导致泄漏;
首先先给项目清单项目的基本结构:
control里面分别是3个窗口的控制器,util里面是窗口(stage)的管理类,resoureces资源文件夹里面是3个窗口的界面fxml文件;
项目源文件下载地址:点击打开链接
程序入口清单:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
Parent root = FXMLLoader.load(Thread.currentThread().getContextClassLoader().getResource("Index.fxml"));
primaryStage.setTitle("第一个窗口");
primaryStage.setScene(new Scene(root));
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
首先需要有stage管理类就叫他StageManager一目了然,里面只有两个静态属性,一个是stage的map,一个是控制器的map,
StageManager清单:
package util;
import java.util.HashMap;
import java.util.Map;
import javafx.stage.Stage;
public class StageManager {
public static Map STAGE=new HashMap();
public static Map CONTROLLER=new HashMap();
}
有了舞台管理器后我们就可以开始编写我们的控制器
首先先看控制器index的清单:
package control;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.stage.Stage;
import util.StageManager;
public class IndexControl implements Initializable{
@FXML
private Button btnOpenTwoWin;
@FXML
public TextField txtData;
@Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
/**
* 打开第二个窗口
* @param event
* @throws Exception
*/
public void open(ActionEvent event) throws Exception {
Stage stage=new Stage();
Parent root = FXMLLoader.load(Thread.currentThread().getContextClassLoader().getResource("Second.fxml"));
stage.setTitle("第二个窗口");
stage.setScene(new Scene(root));
stage.show();
//将第二个窗口保存到map中
StageManager.STAGE.put("second", stage);
//将本窗口保存到map中
StageManager.CONTROLLER.put("indexControl", this);
}
}
首先需要new 一个Stage然后加载fxml文件做一些相关的设置后显示,重点在两个备注中我们将刚刚new出来的stage保存到舞台管理类里面(如果别的窗口不需要对打开的窗口进行操作可以不需要这一步,因为我需要通过按钮关闭窗口,所以需要这一步),
再将第一个窗口的控制器保存到舞台管理类中(这一步是数据交互的时候使用,不需要可以可以不用这一步),这样我们打开第二个窗口就完事,正常显示我们第二个窗口了;
接下来我们来看第二个窗口的控制器清单:
package control;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import util.StageManager;
public class SecondControl implements Initializable {
@FXML
private Button btnOpenThrid;
@FXML
private Button btnCloseThis;
//普通的字符串,不过这里用于将该数据发送到第一个窗口
private String tranDataToIndex;
@Override
public void initialize(URL location, ResourceBundle resources) {
// TODO Auto-generated method stub
}
/**
* 打开第三个窗口
* @param event
* @throws Exception
*/
public void openThrid(ActionEvent event) throws Exception {
Stage stage=new Stage();
Parent root;
root = FXMLLoader.load(Thread.currentThread().getContextClassLoader().getResource("Thrid.fxml"));
stage.setTitle("第三个窗口");
stage.setScene(new Scene(root));
stage.show();
//将第二个窗口保存到map中
StageManager.STAGE.put("thrid", stage);
//将本窗口保存到map中
StageManager.CONTROLLER.put("secondControl", this);
}
/**
* 关闭当前窗口
* @param event
*/
public void closeThis(ActionEvent event) {
if (this.tranDataToIndex!=null) {
IndexControl index=(IndexControl) StageManager.CONTROLLER.get("indexControl");
this.tranDataToIndex="第三个窗口";
index.txtData.setText("来自second:"+this.tranDataToIndex);
}
StageManager.STAGE.get("second").close();
//删除map中的引用
StageManager.STAGE.remove("second");
StageManager.CONTROLLER.remove("indexControl");
}
//--------------get/set---------------
public String getTranDataToIndex() {
return tranDataToIndex;
}
public void setTranDataToIndex(String tranDataToIndex) {
this.tranDataToIndex = tranDataToIndex;
}
}
在这个控制器中有两个重点的地方,第一个就是第一个窗口的控制器private IndexControl index;第二个是closeThis的点击事件方法;
首先如果我们需要数据交互,那么我们就需要将第一个窗口作为第二个窗口的一个属性,因为这个你才能操作第一个窗口;我们可以看到closeThis的点击事件中,如果要传输的数据不为空,那么我们就将舞台控制器里面的第一个窗口的控制器取出来,赋值给当前窗口的index属性,这样我们就可以操作第一个窗口的数据了,在判断里面我们就将第二个窗口的数据设置到第一个窗口的TextField中;
然后我们要通过按钮关闭当前也就是第二个窗口我们就需要将舞台管理器map里面的第二个窗口的舞台取出来,将这个舞台关闭;
到这里多个窗口数据交互与管理就已经结束了;
总结:
第一点也是最重要也是最简单的就是一个舞台管理类,里面两个静态属性一个舞台管理的map,一个是控制器的map;
第二点:将控制器作为另外一个控制器的属性;
第三点:将要被操作的控件改为public共有修饰;
第四点:取出控制器赋值给控制器属性;
第五点:关闭窗口通过取出舞台关闭(不需控件操作直接用X关闭的不需要这点);
完事就这么简单;
最后再给第三个窗口的清单因为第二个窗口的数据来自第三个窗口:
package control;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import util.StageManager;
public class ThridControl implements Initializable {
@FXML
private Button btnCloseThis;
@FXML
private Button btnVoidData;
@Override
public void initialize(URL location, ResourceBundle resources) {
}
/**
* 关闭当前窗口
* @param event
*/
public void closeThis(ActionEvent event) {
StageManager.STAGE.get("thrid").close();
StageManager.STAGE.remove("thrid");
}
/**
* 给第二个窗口数据
*/
public void voidData() {
SecondControl secondControl=(SecondControl) StageManager.CONTROLLER.get("secondControl");
secondControl.setTranDataToIndex("第三个窗口的数据");
//如果本窗口还使用该控制器先不remove这个控制器;
StageManager.CONTROLLER.remove("secondControl");
}
}
运行效果图: