1. 程式人生 > >javafx顯示多個視窗以及javafx多視窗之間資料互動傳輸

javafx顯示多個視窗以及javafx多視窗之間資料互動傳輸

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<String, Stage> STAGE=new HashMap<String, Stage>();
	public static Map<String, Object> CONTROLLER=new HashMap<String, Object>();
}
有了舞臺管理器後我們就可以開始編寫我們的控制器

首先先看控制器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);
    }

}



這個清單裡面有兩個注意點,第一個是TextField因為在第一個視窗要操作這個控制元件所以這個控制元件需要設定為共有的,重點就在在開啟第二個視窗的Open方法中(這個方法btnOpenTwoWin的點選事件)

首先需要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");
        
    }

    
}


執行效果圖: