最終更新日:2021/01/30 原本2017-

JavaによるGUI作成

JavaでGUI (Graphical User Interface)を作成するためのAPIとして の選択肢があります. AWTは古くかあるGUIのAPIですが,OSのウィンドウシステムの部品を使用することから,アプリケーションの見栄えの統一が出来ませんでした. SwingはAWTにとって代わり,ながらくJavaのGUIのAPIとして利用されてきました. JavaFXはSwingにかわる新しいAPIです. JavaFXがSwingと異なる最大の特徴はXMLとCSSでGUIをデザインできる点です.
ここでは,JavaFXを利用して簡単なGUIを作成してみましょう. ただし,JavaFXの特徴であるXMLとCSSを用いたFXMLを利用せずに,SwingでのGUI作成に近い形式で作成します. Java8ではJavaFX8が標準で導入されています.

ウィンドウの起動

まず,最も簡単なGUIをJavaFXで作成してみましょう.
下記のDrawShapesImp.javaをコピーし,動作を確認してみましょう.

DrawShapesImp.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.stage.Stage;
 
public class DrawShapesImp extends Application {
    /**
     * startメソッドのオーバーライド
     *
     * @param primaryStage Stageのインスタンス
     * @throws Exception
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        // JavaFXの起動のために必須
        launch(args);
    }
}
6行目
Applicationクラスを継承しています.
15行目
抽象メソッドであるstart()メソッドをオーバーライドしています.
17行目
ステージを表示しています.
22行目
mainメソッド内で,JavaFXアプリケーションを起動しています.

出力

JavaFXのアプリケーションを作成するためには,Applicationクラスを継承する必要があります. JavaFXの起動から終了までの手順は以下の通りです.
  1. 指定されたApplicationクラスを継承したクラスのインスタンスを生成
  2. init()メソッドの呼び出し
  3. start(java.fx.stage.Stage)メソッドの呼び出し
  4. 次のいずれかの処理が行われるまで待機
  5. stop()メソッドの呼び出し
Applicationクラスは抽象メソッドであるstartメソッドを継承しなければなりません.
JavaFXアプリケーションの起動のために,mainメソッドでlaunch()を呼び出さなければなりません.
これだけだと,ウィンドウが1つ起動されただけなので,次に,ウィンドウ上に描画していきましょう.

図形の描画

JavaFXでは描画するもの(ノードと呼びます)をシーングラフと呼ばれるものに貼り付けて描画する仕組みになっています. その中にルートになるノードが必要で,ルートノードはParentクラスのサブクラス(直系クラスとしてはGroupかRegionかWebView)のインスタンスである必要があります. シーンはSceneクラスのインスタンスとして生成され,生成される際に,ルートノードを介する必要があります. そして,シーンをJavaFXの最上位コンテナであるステージに貼り付けて,それをshow()メソッドで描画することで表示されます.
ここでは,400*400pxのシーンを作成し,線分を一本描画してみましょう.
DrawShapesImp.javaに下記を追加し,動作を確認してみましょう.

DrawShapesImp.javaへの追加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.shape.Line;
import javafx.stage.Stage;
 
public class DrawShapesImp extends Application {
 
    /**
     * startメソッドのオーバーライド
     *
     * @param primaryStage Stageのインスタンス
     * @throws Exception
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageタイトル設定
        primaryStage.setTitle("DrawShapesImp");
        // Groupのインスタンスrootを生成
        Group root = new Group();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root, 400, 400);
 
        // Lineのインスタンスline1を生成
        Line line1 = new Line();
        // 始点設定
        line1.setStartX(0);
        line1.setStartY(0);
        // 終点設定
        line1.setEndX(100);
        line1.setEndY(100);
        // rootの子のリストにline1を追加
        root.getChildren().add(line1);
 
        // primaryStageにsceneを登録
        primaryStage.setScene(scene);
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        // JavaFXの起動のために必須
        launch(args);
    }
}
22行目
ルートノードをGroupのインスタンスで生成しています.
24行目
400*400pxのシーンを生成しています.
27-33行目
線のインスタンスを生成,始点と終点を設定しています.
35行目
ルートノードに線を追加しています.
38行目
ステージにシーンを貼り付けています.

出力

次に,他にも幾つか図形を追加してみましょう.
DrawShapesImp.javaに下記を追加し,動作を確認してみましょう.

DrawShapesImp.javaへの追加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
 
public class DrawShapesImp extends Application {
 
    private Rectangle rectangle = null;
 
    /**
     * startメソッドのオーバーライド
     *
     * @param primaryStage Stageのインスタンス
     * @throws Exception
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageタイトル設定
        primaryStage.setTitle("DrawShapesImp");
        // Groupのインスタンスrootを生成
        Group root = new Group();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root, 400, 400);
 
        // Lineのインスタンスline1を生成
        Line line1 = new Line();
        // 始点設定
        line1.setStartX(0);
        line1.setStartY(0);
        // 終点設定
        line1.setEndX(100);
        line1.setEndY(100);
        // rootの子のリストにline1を追加
        root.getChildren().add(line1);
 
        // Lineのインスタンスline2を生成(始点,終点設定)
        Line line2 = new Line(10, 10, 110, 10);
        // line2の色を青に設定
        line2.setStroke(Color.BLUE);
        // line2の幅を5pxに設定
        line2.setStrokeWidth(5);
        // Circleのインスタンスcircleを生成(中心点,半径)
        Circle circle = new Circle(100, 100, 20);
        // circleを赤で塗りつぶす
        circle.setFill(Color.RED);
        // rootの子のリストにline2とcircleを追加
        root.getChildren().addAll(line2, circle);
 
        // Rectangleのインスタンスrectangleを生成(左上座標と幅,高さ指定)
        rectangle = new Rectangle(200, 200, 50, 30);
        // rootの子のリストにrectangleを追加
        root.getChildren().add(rectangle);
 
        // primaryStageにsceneを登録
        primaryStage.setScene(scene);
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        // JavaFXの起動のために必須
        launch(args);
    }
}
43行目
Lineクラスのインスタンス生成時に始点,終点を指定する事もできます.
45-47行目
線の色や太さを設定することも出来ます.
49-51行目
Circleクラスのインスタンスを中心座標と半径を指定して生成し,色を赤く塗りつぶしています.
53行目
複数のノードをまとめてルートノードに登録することもできます.
56-58行目
Rectangleクラスのインスタンスを左上座標と幅,高さを指定して生成し,登録しています.

出力

JavaFXの2次元座標系では,画面左上隅が原点で右方向がx軸正方向,下方向がy軸正方向です.
ここまで登場したクラスの継承関係は以下の通りです.

マウスイベント処理

GUIアプリケーションの作成において,これまでのように静止画的なもの(JavaFXでは3Dオブジェクトやアニメーションの描画も可能です)だけでなく,ユーザの操作を取り込んだものも必要になります. ユーザの操作はプラットホームに依存しますが,代表的なものに,マウス操作とキーボード操作があります. スマートフォンのようなタッチ操作なども対応出来ます. ここでは,JavaFXでのマウス操作について学びましょう. マウス操作と一言で言っても,クリックやドラッグ,ドロップなど様々あります. これらはアプリケーションで厳密に区別されます. ここでは,DrawShapesImp.javaで作成したRectangleクラスのインスタンス(黒い矩形)をマウスで操作してみましょう. 操作のイメージはその矩形をドラッグしている間,マウスカーソルの動きに矩形がついてくるような動作です. Javaでのマウスやキーボードのイベント処理は典型的に があります. ここでは,最初の2つの方法を説明します.
DrawShapesImp.javaに下記を追加し,動作を確認してみましょう.

DrawShapesImp.javaへの追加

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
 
public class DrawShapesImp extends Application implements EventHandler<MouseEvent> {
 
    private Rectangle rectangle = null;
 
    /**
     * startメソッドのオーバーライド
     *
     * @param primaryStage Stageのインスタンス
     * @throws Exception
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageタイトル設定
        primaryStage.setTitle("DrawShapesImp");
        // Groupのインスタンスrootを生成
        Group root = new Group();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root, 400, 400);
 
        // Lineのインスタンスline1を生成
        Line line1 = new Line();
        // 始点設定
        line1.setStartX(0);
        line1.setStartY(0);
        // 終点設定
        line1.setEndX(100);
        line1.setEndY(100);
        // rootの子のリストにline1を追加
        root.getChildren().add(line1);
 
        // Lineのインスタンスline2を生成(始点,終点設定)
        Line line2 = new Line(10, 10, 110, 10);
        // line2の色を青に設定
        line2.setStroke(Color.BLUE);
        // line2の幅を5pxに設定
        line2.setStrokeWidth(5);
        // Circleのインスタンスcircleを生成(中心点,半径)
        Circle circle = new Circle(100, 100, 20);
        // circleを赤で塗りつぶす
        circle.setFill(Color.RED);
        // rootの子のリストにline2とcircleを追加
        root.getChildren().addAll(line2, circle);
 
        // Rectangleのインスタンスrectangleを生成(左上座標と幅,高さ指定)
        rectangle = new Rectangle(200, 200, 50, 30);
        // rootの子のリストにrectangleを追加
        root.getChildren().add(rectangle);
        // rectangleがドラッグされた時のイベント処理を自身に指定
        rectangle.setOnMouseDragged(this);
 
        // primaryStageにscenceを登録
        primaryStage.setScene(scene);
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        // JavaFXの起動のために必須
        launch(args);
    }
 
    /**
     * handleメソッドのオーバーライド
     *
     * @param event マウスイベント
     */
    @Override
    public void handle(MouseEvent event) {
        // rectangleの左上座標をドラッグ時のカーソルの座標に設定
        rectangle.setX(event.getX());
        rectangle.setY(event.getY());
    }
}
14行目
マウスイベント用のイベントハンドラを実装しています.
62行目
Rectangleクラスのインスタンスrectangleにマウスがドラッグされた時にその事を自分自身に知らせるようにイベントハンドラーを登録
81-85行目
EventHandlerインタフェースの抽象メソッドであるhandle()メソッドを実装しています. このメソッドは,rectangleがドラッグされカーソルが移動する度に呼び出されます. 引数のeventにはマウスドラッグ時の情報が入っているので,そこからマウスカーソルの座標を取り出し,それをrectangleの左上の座標に設定しています.
もう一つ別のイベント処理の方法として匿名クラスを用いた方法を説明します.
下記のDrawShapesAno.javaをコピーし,動作を確認してみましょう.

DrawShapesAno.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
 
public class DrawShapesAno extends Application {
 
    private Rectangle rectangle = null;
 
    /**
     * startメソッドのオーバーライド
     *
     * @param primaryStage Stageのインスタンス
     * @throws Exception
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageタイトル設定
        primaryStage.setTitle("DrawShapesAno");
        // Groupのインスタンスrootを生成
        Group root = new Group();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root, 400, 400);
 
        // Lineのインスタンスline1を生成
        Line line1 = new Line();
        // 始点設定
        line1.setStartX(0);
        line1.setStartY(0);
        // 終点設定
        line1.setEndX(100);
        line1.setEndY(100);
        // rootの子のリストにline1を追加
        root.getChildren().add(line1);
 
        // Lineのインスタンスline2を生成(始点,終点設定)
        Line line2 = new Line(10, 10, 110, 10);
        // line2の色を青に設定
        line2.setStroke(Color.BLUE);
        // line2の幅を5pxに設定
        line2.setStrokeWidth(5);
        // Circleのインスタンスcircleを生成(中心点,半径)
        Circle circle = new Circle(100, 100, 20);
        // circleを赤で塗りつぶす
        circle.setFill(Color.RED);
        // rootの子のリストにline2とcircleを追加
        root.getChildren().addAll(line2, circle);
 
        // Rectangleのインスタンスrectangleを生成(左上座標と幅,高さ指定)
        rectangle = new Rectangle(200, 200, 50, 30);
 
        // rootの子のリストにrectangleを追加
        root.getChildren().add(rectangle);
        // rectangleがドラッグされた時のイベント処理を設定
        rectangle.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                // rectangleの左上座標をドラッグ時のカーソルの座標に設定
                rectangle.setX(event.getX());
                rectangle.setY(event.getY());
            }
        });
 
        // primaryStageにsceneを登録
        primaryStage.setScene(scene);
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        // JavaFXの起動のために必須
        launch(args);
    }
}
14行目
DrawShapesImp.javaと異なり,EventHandlerインタフェースを実装していません.
63-70行目
rectangleにsetOnMouseDragged()メソッドでドラッグ時のイベントハンドラを登録する際に,EventHandlerインタフェースを匿名クラスとして実装したものを直接書いています.
EventHandlerインタフェースは関数型インタフェースなので,ラムダ式としても使用できます.
DrawShapesAno.javaを修正し,動作を確認してみましょう.

DrawShapesAno.javaの修正

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
 
public class DrawShapesAno extends Application {
 
    private Rectangle rectangle = null;
 
    /**
     * startメソッドのオーバーライド
     *
     * @param primaryStage Stageのインスタンス
     * @throws Exception
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageタイトル設定
        primaryStage.setTitle("DrawShapesAno");
        // Groupのインスタンスrootを生成
        Group root = new Group();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root, 400, 400);
 
        // Lineのインスタンスline1を生成
        Line line1 = new Line();
        // 始点設定
        line1.setStartX(0);
        line1.setStartY(0);
        // 終点設定
        line1.setEndX(100);
        line1.setEndY(100);
        // rootの子のリストにline1を追加
        root.getChildren().add(line1);
 
        // Lineのインスタンスline2を生成(始点,終点設定)
        Line line2 = new Line(10, 10, 110, 10);
        // line2の色を青に設定
        line2.setStroke(Color.BLUE);
        // line2の幅を5pxに設定
        line2.setStrokeWidth(5);
        // Circleのインスタンスcircleを生成(中心点,半径)
        Circle circle = new Circle(100, 100, 20);
        // circleを赤で塗りつぶす
        circle.setFill(Color.RED);
        // rootの子のリストにline2とcircleを追加
        root.getChildren().addAll(line2, circle);
 
        // Rectangleのインスタンスrectangleを生成(左上座標と幅,高さ指定)
        rectangle = new Rectangle(200, 200, 50, 30);
 
        // rootの子のリストにrectangleを追加
        root.getChildren().add(rectangle);
        // rectangleがドラッグされた時のイベント処理を設定
        rectangle.setOnMouseDragged((MouseEvent event) -> {
            // rectangleの左上座標をドラッグ時のカーソルの座標に設定
            rectangle.setX(event.getX());
            rectangle.setY(event.getY());
        });
 
        // primaryStageにsceneを登録
        primaryStage.setScene(scene);
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        // JavaFXの起動のために必須
        launch(args);
    }
}

キーイベント処理

DrawShapesAno.javaを修正し,動作を確認してみましょう.

DrawShapesAno.javaの修正

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.stage.Stage;
 
public class DrawShapesAno extends Application {
 
    private Rectangle rectangle = null;
 
    /**
     * startメソッドのオーバーライド
     *
     * @param primaryStage Stageのインスタンス
     * @throws Exception
     */
    @Override
    public void start(Stage primaryStage) throws Exception {
        // Stageタイトル設定
        primaryStage.setTitle("DrawShapesAno");
        // Groupのインスタンスrootを生成
        Group root = new Group();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root, 400, 400);
 
        // Lineのインスタンスline1を生成
        Line line1 = new Line();
        // 始点設定
        line1.setStartX(0);
        line1.setStartY(0);
        // 終点設定
        line1.setEndX(100);
        line1.setEndY(100);
        // rootの子のリストにline1を追加
        root.getChildren().add(line1);
 
        // Lineのインスタンスline2を生成(始点,終点設定)
        Line line2 = new Line(10, 10, 110, 10);
        // line2の色を青に設定
        line2.setStroke(Color.BLUE);
        // line2の幅を5pxに設定
        line2.setStrokeWidth(5);
        // Circleのインスタンスcircleを生成(中心点,半径)
        Circle circle = new Circle(100, 100, 20);
        // circleを赤で塗りつぶす
        circle.setFill(Color.RED);
        // rootの子のリストにline2とcircleを追加
        root.getChildren().addAll(line2, circle);
 
        // Rectangleのインスタンスrectangleを生成(左上座標と幅,高さ指定)
        rectangle = new Rectangle(200, 200, 50, 30);
 
        // rootの子のリストにrectangleを追加
        root.getChildren().add(rectangle);
        // rectangleがドラッグされた時のイベント処理を設定
        rectangle.setOnMouseDragged(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                // rectangleの左上座標をドラッグ時のカーソルの座標に設定
                rectangle.setX(event.getX());
                rectangle.setY(event.getY());
            }
        });
 
        // Textのインスタンスtextを生成(位置,文字列)
        Text text = new Text(100, 100, "Hello Java FX!");
        // rootの子のリストにtextを追加
        root.getChildren().add(text);
        // sceneにKeyPressedイベントを登録
        scene.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {               
                if (event.getText().equals("b")) { // 押下されたキーが"b"と等しければ
                    text.setFont(new Font(text.getFont().getSize() + 1)); // フォントサイズを現在のフォントサイズに1足したものに変更
                } else if (event.getText().equals("s")) { // 押下されたキーが"s"と等しければ
                    text.setFont(new Font(text.getFont().getSize() - 1)); // フォントサイズを現在のフォントサイズから1引いたものに変更
                }
            }
        });
 
        // primaryStageにsceneを登録
        primaryStage.setScene(scene);
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        // JavaFXの起動のために必須
        launch(args);
    }
}

出力

ユーザイベントの実装方法は様々な意見がありますが,色々勉強して,適宜適切な方法を選択するようにして下さい.

マウスイベント・キーイベント処理の詳細

ノードに設定できるマウスイベントとキーイベントは以下のものがあります. マウスイベントやキーイベント以外にもJavaFX 8が対応しているイベントは以下のようなものがあります.

javafx.scene.controlパッケージとjavafx.scene.layoutパッケージ

javafx.scene.controlパッケージにはJava FXで利用可能な様々なGUI部品が用意されています. ボタンやテキストフィールドといった基本的な部品から,メニューやダイアログ,FileChooserやDatePickerなどの便利な部品も既に用意されており簡単に使うことが出来ます.
また,javafx.scene.layoutパッケージにはGUI部品のレイアウトをサポートする有用なクラスが提供されています. レイアウトの種類は以下のようなものがあります.
クラス名説明
BorderPane上、左、右、下、中央の各位置に子をレイアウトします
HBox単一の水平行に子をレイアウトします
VBox単一の垂直列に子をレイアウトします
StackPane下から上へのスタックに子をレイアウトします
GridPane行と列の柔軟なグリッド内に子をレイアウトします
FlowPaneフローペインの境界で折り返されるフローに子をレイアウトします
TilePane均一サイズのタイルのグリッドに子をレイアウトします
AnchorPane子ノードの枠をアンカー・ペインの枠からのオフセット位置までアンカーします
以下のLayoutTest.javaをコピーし,動作を確認してみましょう.

LayoutTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package jp.ac.utsunomiya_u.is;
 
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
 
public class LayoutTest extends Application {
 
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageのタイトルを設定
        primaryStage.setTitle(getClass().getName());
        // BorderPaneのインスタンスrootを生成
        BorderPane root = new BorderPane();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root);
        // primaryStageにscenceを登録
        primaryStage.setScene(scene);
 
        // TextAreaのインスタンスtextAreaを生成
        TextArea textArea = new TextArea();
        // textAreaを編集不可に設定
        textArea.setEditable(false);
        // TextFieldのインスタンスtextFieldを生成
        TextField textField = new TextField();
        // Buttonのインスタンスbuttonを生成(表示文字を引数で指定)
        Button button = new Button("submit");
        // buttonが押下された時のイベントを設定
        button.setOnAction((ActionEvent event) -> {
            // textFieldに入力された文字をgetTextで取得し,appendTextでtextAreaに追記
            textArea.appendText(textField.getText() + "\n");
            // textFieldをクリア
            textField.clear();
        });
 
        // textAreaをTopに貼付け
        root.setTop(textArea);
        // textFieldをCenterに貼付け
        root.setCenter(textField);
        // buttonをRightに貼付け
        root.setRight(button);
 
        // primaryStageを表示
        primaryStage.show();
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}
19行目
BorderPaneクラスのインスタンスを作成しています.
21行目
BorderPaneクラスはParentクラスのサブクラスなのでルート・ノードになる事ができます.

出力

javafx.concurrentパッケージ

Java FXのアプリケーションの中心的存在であるシーン・グラフはスレッドセーフではなく,メインのスレッドのみからアクセスしたり変更したりできます. しかし,GUIアプリケーションでも時間のかかる処理や動作をともなう処理が必要になってきた時に,それに適切に対処する仕組みがあることが望ましいです. 例えば,ボタンを押して何かの処理が始まってしまうと描画が固まってしまって,操作が何もできなくなってしまうアプリケーションはあまり良いアプリケーションではありません. このような場合,GUIの描画や操作のためのメインスレッドと別のタスクのためのスレッドを分けて,別タスクのスレッドはバックグラウンドで処理できることが望ましいと考えられます. これを実現するためのJava FX用のマルチスレッドプログラミングのためのフレームワークとしてjavafx.concurrentパッケージが利用できます.
WorkerインターフェースバックグラウンドスレッドがUIと通信するために利用
Taskクラスバックグラウンド処理をcallメソッドをオーバーライドすることで実装.再利用不可
Serviceクラス1つ以上のバックグラウンドスレッド上でTaskのインスタンスを実行可能.再利用可
以下のConcurrentTest.javaをコピーし,動作を確認してみましょう.

ConcurrentTest.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
package jp.ac.utsunomiya_u.is;
 
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import javafx.application.Application;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.event.ActionEvent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
 
public class ConcurrentTest extends Application {
 
    @Override
    public void start(Stage primaryStage) throws Exception {
        // primaryStageのタイトル設定
        primaryStage.setTitle(getClass().getName());
        // Groupのインスタンスrootを生成
        VBox root = new VBox();
        // Sceneのインスタンスsceneを生成
        Scene scene = new Scene(root);
        // primaryStageにscenceを登録
        primaryStage.setScene(scene);
 
        // Buttonのインスタンスbuttonを生成
        Button button = new Button("start");
        // Labelのインスタンスlabelを生成
        Label label = new Label();
        // ProgressBarのインスタンスprogressBarを生成
        ProgressBar progressBar = new ProgressBar(0);
        // buttonとlabelとprogressBarをrootに貼付け
        root.getChildren().addAll(button, label, progressBar);
 
        // buttonにイベント処理規定
        button.setOnAction((ActionEvent event) -> {
            // Taskのインスタンスtask生成
            Task<Void> task = getTask();
 
            // labelのtextプロパティにtaskのmessageプロパティをバインド
            label.textProperty().bind(task.messageProperty());
            // pregressBarのprogressプロパティにtaskのprogressプロパティをバインド
            progressBar.progressProperty().bind(task.progressProperty());
 
            // スレッドプールをnewSingleThreadExecutorで生成
            ExecutorService executorService = Executors.newSingleThreadExecutor();
            // taskの実行
            executorService.submit(task);
            // スレッドプールの終了
            executorService.shutdown();
 
            primaryStage.setOnCloseRequest((WindowEvent event1) -> { // ウィンドウが閉じられたなら
                // taskをキャンセル
                task.cancel();
            });
        });
 
        // Buttonのインスタンスbutton2を生成
        Button button2 = new Button("start");
        // Labelのインスタンスlabel2を生成
        Label label2 = new Label();
        // ProgressIndicatorのインスタンスprogressIndicatorを生成
        ProgressIndicator progressIndicator = new ProgressIndicator(0);
        // buttonとlabelとprogressIndicatorをrootに貼付け
        root.getChildren().addAll(button2, label2, progressIndicator);
 
        // button2にイベント処理規定
        button2.setOnAction((ActionEvent event) -> {
            // Serviceのインスタンスserviceを生成
            Service<Void> service = new Service<Void>() {
                // 抽象メソッドcreateTaskをオーバーライド
                @Override
                protected Task<Void> createTask() {
                    // Taskのインスタンスを返す
                    return getTask();
                }
            };
            // serviceを開始
            service.start();
            // label2のtextプロパティにserviceのmessageプロパティをバインド
            label2.textProperty().bind(service.messageProperty());
            // pregressIndicatorのprogressプロパティにserviceのprogressプロパティをバインド
            progressIndicator.progressProperty().bind(service.progressProperty());
        });
 
        // primaryStageを表示
        primaryStage.show();
    }
 
    /**
     * Taskのインスタンス生成
     *
     * @return Taskのインスタンス
     */
    private Task<Void> getTask() {
        return new Task<Void>() {
            // 抽象メソッドcallをオーバーライド
            @Override
            protected Void call() throws Exception {
                for (double i = 0; i <= 1.01; i += 0.01) {
                    // messageプロパティを更新
                    updateMessage(String.format("%2d%%", (int) (i * 100)));
                    // progressプロパティを更新
                    updateProgress(i, 1.0);
                    // 100[ms]一時停止
                    Thread.sleep(100);
                }
                // messageプロパティを更新
                updateMessage("finish");
                return null;
            }
        };
    }
 
    public static void main(String[] args) {
        launch(args);
    }
}

出力