javafxでGUI

VsCodeでMavenを使ってjavafxでGUIを作ってみる

  1. Scene Builderをインストールする
  2. javafxをMavenで作る ひな形作成
  3. 1画面の計算プログラムを作ってみる
  4. 画面どうしでデータ渡しをしてみる

いままでCUIのjavaばかりでしたので、Servletを使わずにGUIプログラムを作ってみましょう

Scene Builderをインストールする

まずは、Scene Builderをインストールしましょう。このツールを入れると画面の作成をVBのようにVisualにすることができるようになります

まずは、Scene Builderをダウンロードします

scenebuilderダウンロード
https://gluonhq.com/products/scene-builder/

このサイトからwindows版をダウンロードします

scenebuilderダウンロード

それほど大きくはありません。

scenebuilderダウンロード

ダウンロードしたらダブルクリックしてインストールしてください

scenebuilderインストール scenebuilderインストール scenebuilderインストール scenebuilderインストール scenebuilderインストール

インストールが終わると、ユーザーの配下にインストールされます。この場所はあとで使います

scenebuilderインストール

scene builderをインスタールしたら、VsCodeに拡張機能で「Scene builder extension for Visual Studio Code」をインストールします。

scenebuilderインストール

さて、ここでこのインストールしたscenebuilderとVsCodeを接続していきます。Vscodeを起動してCTRL+SHIFT+Pでコマンドパネルを表示して

VsCode設定

Configure Scene Builder path と入力して、表示されたその項目をクリックすると

VsCode設定

どこにscene builder.exeがあるか聞いてくるので、さきほど見たユーザーの配下のscenebuilderをクリックします。これでvscodeから直接scene builderが起動できるようになります。

javafxをmavenで作る

さて、vscodeでmavenを利用してjavafxを作っていきます。CTRL+SHIFT+Pを押して create java projectを選びます

javafx作成

あとは、mavenの回でやったようにプロジェクト名等を入れていきます

今回はjavafxのサンプルを作りますので、javafxを選択します

javafx作成

group idを入れます。これがpackage名になりますので、自分のドメイン名(架空でかまわない)を逆順にした名前を英数小文字で入力してください。これがパッケージになりますので、フォルダーの階層になります。

javafx作成

続いて、プロジェクト名を英数小文字で入力します。これはフォルダー名とは別です。今回は画像とはことなりますが「scenesample」として作ってみましょう

javafx作成

その後に、そのフォルダーの先頭をどこに作るのか聞いてきますので、自分がmaven等で作成しているworkspaceにフォルダーを作成、あるいは選択してください。これは大文字が入っても大丈夫です。今回は「SceneSample」として作りましょう

javafx作成

作成したらそのフォルダーを選択します

javafx作成

するとmavenが働いてフォルダー作成の確認を求めてきますので、ここでこの当たりをマウスでクリックしておいてからエンターキーを押します

javafx作成

つづいてYの文字が出てきたらそこに小文字でyとenterを入力します

javafx作成

そのあと、今作った場所をウィンドウを開くか聞かれたらopenを押します。すると今作ったフォルダーが新たに開かれます。openを押さないでその画面で続けたい場合は×で終了して、enterキーを入力すればその場で作成を続けることも可能です。

javafx作成

新たなウィンドウではフォルダーが展開されていませんので、開かないとわかりません

javafx作成

srcを開くと、フォルダーが指示通りに作成されているのがわかります。

javafx作成

srcに3つのjavaソースが出来ているのがわかります。App.javaがmainでここからprimaryController.javaが呼び出されます。そこでボタンを押すことによってsecondaryController.javaが起動されて画面が切り替わるようになっています。つまりこのjavafxの作成の流れで2つの画面をもったサンプルが作成されることがわかります

中身を見てみましょう

App.java
	package jp.co.etlab;

	import javafx.application.Application;
	import javafx.fxml.FXMLLoader;
	import javafx.scene.Parent;
	import javafx.scene.Scene;
	import javafx.stage.Stage;
	
	import java.io.IOException;
	
	/**
		* JavaFX App
		*/
	public class App extends Application {
	
		private static Scene scene;
	
		@Override
		public void start(Stage stage) throws IOException {
			scene = new Scene(loadFXML("primary"), 640, 480);
			stage.setScene(scene);
			stage.show();
		}
	
		static void setRoot(String fxml) throws IOException {
			scene.setRoot(loadFXML(fxml));
		}
	
		private static Parent loadFXML(String fxml) throws IOException {
			FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
			return fxmlLoader.load();
		}
	
		public static void main(String[] args) {
			launch();
		}
	
	}

まずjavafxのウィンドウの構造を説明します

javafxの構造

一番外側の枠にあたるのが「stage」です。この中の台紙の部分が「scene」になります。この上にパネルとして「pane」があり、そう上に部品が乗るという感じになります。

javaではmainが最初に起動されますのでlaunch()が起動されます。これによってApplicationクラスが動きjavafxが起動されます。startメソッドがその起動時にコールされるもので、ここで初期化を行います。

起動時の枠としてstageが渡されるので、そこにprimary.fxmlで宣言された画面構造が台紙として宣言され、その台紙をstageにsetして、表示showします。

続いて第1画面の説明です。画面ファイルはサンプルとして2つ用意されています。

javafxの構造

resourcesの配下のパッケージ配下に2つのfxmlファイルがあります。

primary.fxml
	<?xml version="1.0" encoding="UTF-8"?>

	<?import javafx.scene.layout.VBox?>
	<?import javafx.scene.control.Label?>
	<?import javafx.scene.control.Button?>
	<?import javafx.geometry.Insets?>
	
	<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" 
		fx:controller="jp.co.etlab.PrimaryController">
		<children>
			<Label text="Primary View" />
			<Button fx:id="primaryButton" text="Switch to Secondary View" onAction="#switchToSecondary"/>
		</children>
		<padding>
			<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
		</padding>
	</VBox>

javafxで画面がshowされると、その中の「fx:controller=」で宣言されたパッケージがコントロールを受け持つことになります。

でもこれでは画像がどう出るのかわかりません。そこでscenebuilderの登場です。見たいfxmlファイル上で右クリックして「open in scene builder」をクリックします

scenebuilderの起動

これが表示された画面です。といっても宣言されているのはペインPaneにあたる部分から下の階層です。stageやsceneはプログラムで書きます。

scenebuilderの起動

中身を見ていきましょう。ボタンをクリックします。右側のcodeの中身を展開すると

scenebuilderの起動

「fx.id」にprimaryButtonとあります。これがjavafxで宣言したボタンの名前です。プログラム中で使用できます。また「onAction」にswicthToSecondaryとありますが、これがボタンをクリックしたときにリンクされるメソッドの名前で、これをプログラム中に記述します。

ということで、この画面のプログラムを見てみましょう

primaryController.java
	package jp.co.etlab;

	import java.io.IOException;
	import javafx.fxml.FXML;
	
	public class PrimaryController {
	
		@FXML
		private void switchToSecondary() throws IOException {
			App.setRoot("secondary");
		}
	}

画面が呼び出されても、このプログラムはなにも動きません。ただボタンが押されるとそこに宣言してあった「switchToSecondary」メソッドが起動されて、プログラムが動きます。書かれているのは、同じstageを使って、secondary.fxmlをsceneに貼って動けという指示です。secondary.fxmlの中身を見てみましょう

secondary.fxml
	<?xml version="1.0" encoding="UTF-8"?>

	<?import javafx.scene.layout.VBox?>
	<?import javafx.scene.control.Label?>
	<?import javafx.scene.control.Button?>
	<?import javafx.geometry.Insets?>
	
	<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" 
	fx:controller="jp.co.etlab.SecondaryController">
		<children>
			<Label text="Secondary View" />
			<Button fx:id="secondaryButton" text="Switch to Primary View" onAction="#switchToPrimary" />
		</children>
		<padding>
			<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
		</padding>
	</VBox>

最初の画面とほぼ同じで、secondarycontrollerをコントローラーとすること、ボタンが押されたら switchtoprimaryが呼ばれることが書いてあります。

同様にscenebuilderで見てみましょう

scenebuilderの起動

同様で、ボタンを押すとprimaryに戻るようになっています。また左の階層をみるとPaneの一種のVBOXという部品を縦に並べる性質のあるPaneの中に中央揃えでlabelとbuttonが入っているので、縦に部品が並んでいるのがわかります。HBOXでは横に並びます

では、このままプログラムをコンパイル実行してみましょう

mavenでコンパイル

mainの上にrunがありますので、ここをクリック

mavenで実行 mavenで実行 mavenで実行

ボタンを押すたびにscaneの中身が書き変わります

計算プログラムを作ってみる

まずは、前回のjavafxのサンプルを作り、そのあと不要なsecondarycontroller.javaとsecond.fxmlを消します。

計算プログラムの画面を作ります

primary.fxml
	<?xml version="1.0" encoding="UTF-8"?>

	<?import javafx.geometry.Insets?>
	<?import javafx.scene.control.Button?>
	<?import javafx.scene.control.Label?>
	<?import javafx.scene.control.TextField?>
	<?import javafx.scene.layout.HBox?>
	<?import javafx.scene.layout.VBox?>
	
	<VBox alignment="CENTER" spacing="20.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" 
	fx:controller="jp.co.etlab.PrimaryController">
		<children>
			<Label text="整数の足し算を行います" />
			<HBox prefHeight="0.0" prefWidth="200.0">
				<children>
				<TextField fx:id="arg1" prefHeight="26.0" prefWidth="55.0" />
				<Label text="+" />
				<TextField fx:id="arg2" prefHeight="26.0" prefWidth="55.0" />
				<Label text="=" />
				<TextField fx:id="ans" editable="false" prefHeight="26.0" prefWidth="67.0" />
				</children>
			</HBox>
			<HBox alignment="CENTER" prefHeight="0.0" prefWidth="200.0">
				<children>
				<Button fx:id="calcButton" onAction="#calc" text="calc">
					<HBox.margin>
						<Insets right="10.0" />
					</HBox.margin>
				</Button>
				<Button fx:id="closeButton" mnemonicParsing="false" onAction="#close" text="exit" />
				</children>
			</HBox>
		</children>
		<padding>
			<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
		</padding>
	</VBox>

画面を作るコツは、縦に並べるVBOXと横に並べるHBOXを組み合わせて作ることです。今回はVBOXの中にHBOXを入れています。scenebuilderで作るときは左のコンテナーの中のHBOXをドラッグして左下の階層のVBOXの上にドラッグすることです。下にドラッグするとその下に入ってしまいますが、上にドロップするとその配下に入ります。間違っても何回でも移動できるのでやってみましょう。

画面の作成

作ったのは入力が2つ、出力が1つ、ボタンが2つの画面です。縦にVBOXが並び、その中にHBOXを入れて、その中にTextFieldやButtonが並んでいます。

calcボタンをクリックすると「calc」メソッドにつながります。

ではプログラムを作っていきましょう。まずはApp.javaから

App.java
	package jp.co.etlab;

	import javafx.application.Application;
	import javafx.fxml.FXMLLoader;
	import javafx.scene.Parent;
	import javafx.scene.Scene;
	import javafx.stage.Stage;
	
	import java.io.IOException;
	
	/**
		* JavaFX App
		*/
	public class App extends Application {
	
		private static Scene scene;
	
		@Override
		public void start(Stage stage) throws IOException {
			scene = new Scene(loadFXML("primary"), 240, 148);
			stage.setScene(scene);
			stage.setTitle("足し算");
			stage.show();
		}
	
		static void setRoot(String fxml) throws IOException {
			scene.setRoot(loadFXML(fxml));
		}
	
		private static Parent loadFXML(String fxml) throws IOException {
			FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
			return fxmlLoader.load();
		}
	
		public static void main(String[] args) {
			launch();
		}
	
	}

App.javaはほとんど変わっていません。setTitleが増えただけです。

primaryController.java
	package jp.co.etlab;

	import java.io.IOException;
	import javafx.fxml.FXML;
	import javafx.scene.control.Button;
	import javafx.scene.control.TextField;
	import javafx.stage.Stage;
	
	public class PrimaryController {
		@FXML   private TextField arg1;
		@FXML   private TextField arg2;
		@FXML   private TextField ans;
		@FXML   private Button closeButton;
	
		@FXML
		private void calc() throws IOException {
			String strArg1 = arg1.getText();
			String strArg2 = arg2.getText();
	
			Integer intArg1 = 0;
			Integer intArg2 = 0;
			int intAns = 0;
	
			try {
				intArg1 = Integer.parseInt(strArg1);
				intArg2 = Integer.parseInt(strArg2);
				intAns = intArg1 + intArg2;
	
				ans.setText(String.valueOf(intAns));
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
		}
	
		@FXML
		private void close() throws IOException {
			Stage stage = (Stage)closeButton.getScene().getWindow();
			stage.close();
		}
	}

primarycontroller.javaは入力の部分、計算の部分、出力の部分、終了の部分が増えています。

@FXMLを宣言して記述した部分がjavafx用のコーディングです。入力のTextFieldやButtonを宣言します。fx:idで宣言した名前で宣言すれば画面とつながります。

またデータの取得、出力が setText,getTextで行うことができます。文字としてやり取りするので数値にコンバートしてから計算を行います。

終了で少し変なことをしています。closeButtonから逆に元のstageを求めて、stageをcloseすることでjavafxを終了しています。画面を隠したりするのにもこの方法が扱えます。

画面の作成

calcボタンにcalcメソッドをリンクしています。

コンパイルして実行してみましょう。

mavenで実行

TextFieldに数値を入れてcalcボタンを押すと結果が出力されます。計算が出来ないときは画面にはでないでエラーがコマンドに出力されます。calcモジュールやcloseモジュールを書いておけば動きます。exitボタンで終了します。

画面でデータ渡しをしてみる

単純に画面が切り替わるのであればひな形でやっているように、Web同様切り替えることができます。

ただ、VBなどでやっている方法は、どれかメインになる画面を決めておいて、そこから子画面を呼び出して処理が終わったら元のメインに帰るという方法です。こうしておけばメインでの終了がプログラムの終了になります。ばらばらに作ってしまうと終了のタイミングがわからなくなります。

今回はメインとしたprimaryを作り、loginが済んでいないのならばsecondaryに飛んでloginさせてからメインに戻り、login後thirdに飛び処理をしてからprimaryに戻るのをやってみます。図にすると以下のようになります。

プログラム構成

まずはjavafxのひな形を作ります。primary.fxmlを作ります

primary.fxml
	<?xml version="1.0" encoding="UTF-8"?>

	<?import javafx.geometry.Insets?>
	<?import javafx.scene.control.Button?>
	<?import javafx.scene.control.Label?>
	<?import javafx.scene.layout.VBox?>
	
	<VBox alignment="CENTER" prefHeight="162.0" prefWidth="192.0" spacing="20.0" xmlns="http://javafx.com/javafx/20.0.1" 
	xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.co.etlab.PrimaryController">
		<children>
			<Label text="Main" />
			<Button fx:id="loginButton" onAction="#switchToLogin" text="Login" />
			<Button fx:id="helloButton" mnemonicParsing="false" onAction="#switchToThird" text="Hello" />
			<Button fx:id="exitButton" mnemonicParsing="false" onAction="#exitProgram" text="exit" />
		</children>
		<padding>
			<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
		</padding>
	</VBox>

まずは、ボタンを3つに改造します。それぞれ名前をつけて飛び先を宣言して下さい

primary.fxml

続いてlogin画面をsecondaryとして作ります

secondary.fxml
	<?xml version="1.0" encoding="UTF-8"?>

	<?import javafx.geometry.Insets?>
	<?import javafx.scene.control.Button?>
	<?import javafx.scene.control.Label?>
	<?import javafx.scene.control.PasswordField?>
	<?import javafx.scene.control.TextField?>
	<?import javafx.scene.layout.HBox?>
	<?import javafx.scene.layout.VBox?>
	
	<VBox alignment="CENTER" prefHeight="235.0" prefWidth="240.0" spacing="20.0" xmlns="http://javafx.com/javafx/20.0.1" 
	xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.co.etlab.SecondaryController">
		<children>
			<Label text="login window" />
			<HBox prefHeight="22.0" prefWidth="200.0">
				<children>
				<Label text="ID">
					<HBox.margin>
						<Insets right="11.0" />
					</HBox.margin>
					<padding>
						<Insets left="20.0" />
					</padding>
				</Label>
				<TextField fx:id="identer" prefHeight="26.0" prefWidth="151.0" />
				</children>
			</HBox>
			<HBox prefHeight="35.0" prefWidth="200.0">
				<children>
				<Label text="pass">
					<padding>
						<Insets left="20.0" />
					</padding></Label>
				<PasswordField fx:id="passenter" />
				</children>
			</HBox>
			<Button fx:id="loginButton" mnemonicParsing="false" onAction="#loginCheck" text="Login" />
			<Button fx:id="cancelButton" onAction="#switchToPrimary" text="cancel" />
		</children>
	</VBox>

ID,passwordを入力します。idはTextField、passwordはpasswordField、loginボタンとcancelボタンを入れます

secondary.fxml

続いて、3番目の画面です。idをユーザー名として表示するだけです。戻るボタンでメインに戻ります

third.fxml
	<?xml version="1.0" encoding="UTF-8"?>

	<?import javafx.geometry.Insets?>
	<?import javafx.scene.control.Button?>
	<?import javafx.scene.control.Label?>
	<?import javafx.scene.layout.HBox?>
	<?import javafx.scene.layout.VBox?>
	
	<VBox alignment="CENTER" prefHeight="162.0" prefWidth="192.0" spacing="20.0" xmlns="http://javafx.com/javafx/20.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="jp.co.etlab.ThirdController">
		<children>
			<Label text="Third" />
			<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
				<children>
				<Label text="こんにちは" />
				<Label fx:id="iddisp" />
				<Label text="さん" />
				</children></HBox>
			<Button fx:id="returButton" mnemonicParsing="false" onAction="#returnProgram" text="return to main" />
		</children>
		<padding>
			<Insets bottom="20.0" left="20.0" right="20.0" top="20.0" />
		</padding>
	</VBox>

メインに戻ったらexitで終了します。

third.fxml

つづいて、プログラムを説明します。App.javaはひな形のままですので省略します。

つづいて、primaryController.javaです。これをメインの画面としてloginを呼び出したり、子画面を呼び出したりしてみます。

primaryController.java
	package jp.co.etlab;

	import java.io.IOException;
	import javafx.fxml.FXML;
	import javafx.fxml.FXMLLoader;
	import javafx.scene.Parent;
	import javafx.scene.Scene;
	import javafx.scene.control.Button;
	import javafx.stage.Stage;
	
	public class PrimaryController {
		private String id="";
		private String pass="abc";
	
		private Stage stage;
		private Scene scene;
		private Parent root;
	
		@FXML private Button exitButton;
	
		public void setId(String id) {
			this.id = id;
		}
	
		public String getId() {
			return this.id;
		}
	
		@FXML
		private void switchToLogin() throws IOException {
			FXMLLoader loader = new FXMLLoader(getClass().getResource("secondary.fxml"));
			root = loader.load();
			SecondaryController secondaryController = loader.getController();
			secondaryController.setPassword(this.pass); //check用のpassを送る
			secondaryController.setMain(this);
			stage = new Stage();
			scene = new Scene(root);
			stage.setScene(scene);
			stage.showAndWait();
		}
	
		@FXML 
		private void switchToThird() throws IOException {
			if (this.id.isEmpty()) {
				System.out.println("empty");
				return;
			}
			FXMLLoader loader = new FXMLLoader(getClass().getResource("third.fxml"));
			Parent root = loader.load();
			ThirdController thirdController = loader.getController();
			stage = new Stage();
			scene = new Scene(root);
			stage.setScene(scene);
			thirdController.setIdtext(this.id);
			stage.showAndWait();
		}
	
		@FXML 
		private void exitProgram() throws IOException {
			Stage stage = (Stage)exitButton.getScene().getWindow();
			stage.close();
		}
	}

処理の説明をします。起動された時、特になにも行っていません。ボタンが3つ表示されていますが、この状態ではログインが済んでいないので、this.idには空文字が入っているだけです。この状態では「hello」ボタンを押しても、先に進むことはできません。

「login」ボタンが押されて、いったんlogin画面に移動します。そのために「switchTloLogin」メソッドが動きます。ここで次の画面のsecondary.fxmlを読み込んで、そのコントローラであるsecondaryControllerのインスタンスを作ります。このインスタンスを利用してsetPassメソッドを使ってpasswordの正解を送り込んで置きます。

そして、次のコードは無理やりなのですが、自分のインスタンスをsecondaryControllerに送ります。これをしないとsecondaryからprimaryのデータを返すことができなくなります。苦肉の策です。secondaryでprimaryの画面を作れば良いような気がしますが、そこで作ったprimaryとthisのインスタンスは別アドレスの別物です。なのでいまここのアドレスを渡しておく必要がありました。(良い方法を教えてください)

そしてここで作ったsecondaryは処理が移った後のsecondaryと同じものなのでデータはわたります。そしてprimaryからsecondary画面を呼び出して、終わるまでここで待ちます。

処理的にはここからsecondaryに移りますが、説明はログインが終わったとして続けます。ログインが成功するとsecondaryからidに入力された文字列がthis.idに渡ってきています。

すると先程まで「hello」で先にいけなかった部分がロックが外れて「switchToThird」ができるようになります。ThirdControllerのインスタンスを作ったら、そのインスタンスを使ってsetIdTextを行い画面に文字を表示します。ThirdControllerのthis.idは設定はしていますが、表示するタイミングがないのでsetTextを使って表示までやっておきます。こちらもThirdが終わるまでここで待ちます。

つづいて、secondaryControllerです。

secondaryController.java
	package jp.co.etlab;

	import java.io.IOException;
	import javafx.fxml.FXML;
	import javafx.fxml.FXMLLoader;
	import javafx.scene.Parent;
	import javafx.scene.control.Button;
	import javafx.scene.control.PasswordField;
	import javafx.scene.control.TextField;
	import javafx.stage.Stage;
	
	public class SecondaryController {
		@FXML private TextField identer;
		@FXML private PasswordField passenter;
		@FXML Button cancelButton;
	
		private String password;
		private PrimaryController main;
	
		public void setPassword(String pw) {
			this.password = pw;
		}
		public void setMain(PrimaryController a) {
			this.main = a;
		}
	
		@FXML
		private void loginCheck() throws IOException {
			String idname = identer.getText();
			String password = passenter.getText();
	
			//password check
			if (!password.equals(this.password)) {
				return;
			}
			this.main.setId(idname);
			this.switchToPrimary(); //exit
		}
	
		@FXML
		private void switchToPrimary() throws IOException {
			Stage stage = (Stage)cancelButton.getScene().getWindow();
			stage.close();
		}
	}

ここで行うのはidとpasswordに入力です。まず入力してもらってから「login」ボタンを押すと loginCheckメソッドが動きます。ここではあらかじめprimaryから送られているpasswordと入力された文字を比較して正しくなければ何もしません。正しければprimaryControllerのインスタンスを使って入力されたidを返します。

そして、switchToPrimaryを使ってこの画面を閉じます。

ログインに成功しなければあきらめて「cancel」を押してprimaryに戻ります。

つづいて、primaryに戻ってからの「hello」が押されてThird画面に移動します。

thirdController.java
	package jp.co.etlab;

	import java.io.IOException;
	import javafx.fxml.FXML;
	import javafx.scene.control.Button;
	import javafx.scene.control.Label;
	import javafx.stage.Stage;
	
	public class ThirdController {
		private String id;
	
		@FXML private Label iddisp;
		@FXML private Button returButton;
	
		public void setIdtext(String id) {
			this.id = id;
			iddisp.setText(id);
		}
	
		@FXML
		private void returnProgram() throws IOException {
			Stage stage = (Stage)returButton.getScene().getWindow();
			stage.close();
		}
	}

primaryで説明したように、Thirdにidを送って表示までしておきます。なので画面が出たときにすでにidが表示されています。これでidが確認できたので、「return to main」ボタンで画面を閉じます。すると呼び出したprimaryの画面が残っています。

primaryは、仕事が終わったので「exit」ボタンで画面を閉じて処理を終わります。

あとはコンパイルして実行です

実行

「login」を押してlogin画面を表示します。primary画面は消えることなく残っています。

login画面

ここでidとpasswordを入れます。idはそのままユーザー名となり、パスワードは今回はabcで固定です。「login」ボタンを押します

login画面

ログインが成功すればprimaryに戻ります。間違えればなにもおきません。あきらめて「cancel」した場合ユーザー名は登録されません。

mainで「hello」ボタンを押すと今入力したidがユーザー名として表示されます。

hello画面

「return to main」ボタンでmainに戻ります。そして「exit」で終了です