JavaFX アプリケーション

JavaFX 11以上、Scene Builder、Eclipse を利用して


JavaFX プロジェクトの作成、Scene Builder

投稿日時:

最終更新日時:

Java 12、JavaFX 12 に関しては「動機と目的の追記」を参照!

モジュールを利用した簡単なサンプルアプリケーションを作成する。まず、JavaFX プロジェクトを新規作成し、Scene Builder を利用して、GUI(Graphical User Interface)を作り、その後、ボタンなどのコントロールの動作を完成させる。

完成形
完成形

作成する簡単なアプリケーションは、中央に背景色を選ぶトグルボタンを3個と中央を表す Center: と選ばれた色を表示するラベルを配置し、その上に「背景色を選んで下さい」と表示するラベルを配置する。左に Left、下に Bottom、右に Right、と書いてあるラベルを配置し、トグルボタンで選ばれた色、Cyan、Yellow、Pink に応じて、Left、Bottom、Right の背景色がその色に変わる、というものである。上図は Cyan ボタンを押して、Cyan を選び、Left の背景色が Cyan になったところである。このように、上下左右(top、bottom、left、right)と中心(center)の5個の部分の入れ物から出来ている容器を BorderPane と呼ぶ。

Eclipse を起動し、File>New>Others… から

File>New>Others…

JavaFX Project を選び Next> ボタンを押し、

JavaFX Project

Project Name: に、今は、「SampleFX11」を入力し、Next> ボタンを押す。

Project Name

Java Setting ウィンドウではそのまま Next> ボタンを押す。ただし、既定で module-info.java を作ることになっているが、残念ながら作成してくれない。(理由は不明である。モジュールを利用したアプリケーションを作成するので、後で自分で作成する。)また、Libraries タブで JavaFX SDK を設定してもよいが、ここでは設定しないで、後で設定する。

Java Setting

新規プロジェクト作成の最後の設定の Language で FXML、Root-Type で BorderPane を選択し、File Name と Controller Name に、今は、SampleFX11、SampleFX11Controller と入力し、Finish ボタンを押す。

Last Setting

プロジェクト SampleFX11 が作成された。

SampleFX11

先ほどの JavaFX Project の新規作成の最後の設定で、(Scene Builder で編集可能な、FXML で書かれた)SampleFX11.fxml というファイルに、ルートの親容器として BorderPane を選んだことを書き、その親容器の中に配置される(であろう)ボタンなどが押された時の動作を SampleFX11Controller.java に記述する、ということを設定したことになる。

JavaFX 新規プロジェクト作成の最後のプロセス、JavaFX SDK ライブラリの指定(これは、上記の Java Settings ウィンドウの Libraries タブでも行えた)と module-info.java ファイルを作成する。左側の Package Explorer にある、今作成したプロジェクト SampleFX11 を右クリックし一番下にある Properties を選び、Java Build Path を選ぶ。Modulepath を選び、Add External JARs… ボタンを押し、JavaFX 11 SDK の lib ディレクトリ(最初にすること absolute-path-to-JavaFX11SDK\lib )にある8個のjarファイルを選び、「開く」ボタンを押し、Apply and Close ボタンを押す。これで、Eclipse はJavaFX 11 SDK の位置を認識した。

JavaFX 11 SDK

module-info.java を作成するために、Package Explorer の SampleFX11 プロジェクトの下にある src ディレクトリを右クリックし、New>File を選び、File name: に module-info.java と入力し、Finish ボタンを押す。空のファイルが作成されるので、以下の内容を入力する(ここでは、モジュール名を sampleFX11 とした)。

module sampleFX11 {

}
module-info.java

Package Explorer の SampleFX11 プロジェクトを見ると、今までの作業で、今作成した module-info.java 以外に、自動で Main.java、SampleFX11Controller.java(現時点では、コメントだけのファイル)、application.css(現時点では、コメントだけのファイル)、SampleFX11.fxml が作成されていることが分かる。また、Main.java に赤の「×」マークがついており、エラーがあることが分かる

Main.java を(ダブルクリックして)開き、赤の「×」マークがついている import 文の赤の波線をマウスでポイントすると修正の候補が現れるので、Add ‘requres javafx.graphics’ to module-info.java を選ぶ。自動で module-info.java が修正される。

エラーのある Main.java

同様のことを繰り返せば、エラーがなくなる

Main.java

エラーがなくなった Main.java の start 関数の try 文の中を見てみる。(Scene Builder を利用して、後で内容を完成させる)SampleFX11.fxml ファイルに記述されている(BorderPane である)親容器を root に代入し、それから400×400の scene を作成し、それに (後で完成させる)application.css を設定し、scene を stage にセットし stage を表示する。

SampleFX11.fxml を見ると、親容器である BorderPane が定義されているだけであり、fx:controller=”SampleFX11Controller” の部分が SampleFX11.fxml にある部品間の制御を行うのが SampleFX11Controller.java であることを示している。

SampleFX11.fxml

次に、最初の山場である Scene Builder を利用して、上述の 5つの容器からなる BorderPane に完成形を想像しながら、ラベルやトグルボタンを配置する。Scene Builder を起動し、File>Open… から SampleFX11.fxml を開く

Scene Builder で SampleFX11.fxml を開く

Scene Builder は大きく分けて4個の部分、Library パネル、Document パネル、Content パネル、Inspector パネル、に分かれている。左側の上部に Library パネル、下部に Document パネル、中央に Content パネル、右側に Inspector パネルが配置されている。Library パネルにはわれわれが利用可能な部品が、例えば、容器を集めた Containers セクション、ラベルやトグルボタンを集めた Controls セクション、等に分けて保管されている。Document パネルの Hierarchy セクションには現時点での GUI の構造が木構造で示されている。SampleFX11 プロジェクト作成時に BorderPane を指定したので、TOP から BOTTOM までの5個の部分が、何か部品が挿入されるのを待っている。左右中央部分の Content パネルには何も表示されていないが、この BorderPane が幅と高さを持たないからで、以降の操作でその時点での状況が表示される。右側の Inspector パネルの Properties セクション、Layout セクション、Code セクションには、Hierarchy セクションまたは Content パネルで選択されている部品に関する情報が表示され、目的の情報を記入(またはに変更)していく。

まず、TOP の位置にラベルを挿入する。Library パネルの Controls セクションをクリックし、Label を探し、Document パネルの Hierarchy セクションの Insert TOP までドラッグアンドドロップする。TOP の位置に ラベルが挿入された。LEFT、RIGHT、BOTTOM にも同じことを行えばよい。

ラベルの挿入

CENTER の位置には、(横に並んだ3個のトグルボタン)とラベルを縦に並べる。部品を容易に縦に並べるために、VBox という容器を用いる。Library パネルの Containers セクションから VBox を探し、Document パネルの Hierarchy セクションの Insert CENTER までドラッグアンドドロップする。ある広さの VBox が挿入された。

VBox の挿入

現時点では、VBox の大きさが固定されているので、中身に応じて大きさが変わるように、Inspector パネルの Layout セクションの Pref Width と Height を USE_COMPUTED_SIZE に変える。VBox が潰れた

VBox の大きさを自動にする

今挿入した VBox の中に(横に並んだ3個のトグルボタンを容易に並べられるように)HBox とラベルを挿入する。HBox を潰すために、Inspector パネルの Layout セクションの Pref Width と Height を USE_COMPUTED_SIZE に変える。HBox の中に Library パネルの Controls セクションからToggleButtonを3個挿入する。以上で部品の挿入が完了した

部品挿入の完了

次に、見栄えを修正する。現段階では、トグルボタンやラベルが接近しすぎている。部品の周りに余白を入れる方法として、margin(外側の余白)や padding(内側の余白)を操作するようである。まず、トグルボタンの周りに余白を入れるには、margin を(ここでは、上、右、下、左の全てに5ピクセル)を設定する。例えば、一番右のトグルボタンを(左側にある Document パネルの Hierarchy セクションで)選択し、右側にある Inspector パネルの Layout セクションの Margin に全て5を代入する(一番左に5を代入し、その右の>をクリックすればよい)。これでこのトグルボタンの周りに余白が開いた。残りのトグルボタンにも同じことを行えばよい。

トグルボタン マージンの設定

次に、ラベルの周りに余白を開ける。ラベルには margin はなく padding がある。TOP、RIGHT、BOTTOM、LEFT、CENTER の全てのラベルに一度に padding を設定するために、これら5個のラベルを選択する(最初のは単にクリック、2個目以降は、Ctrl キー、または、Shift キーを押しながらクリックする)。その後、Layout セクションの Padding を全て5に設定する。これで、ラベルの文字の周りに余白が開いた

ラベル パディングの設定

現時点では、全てのラベルに Label、全てのトグルボタンに ToggleButton と表示されている。これを「背景色を選んで下さい」、「Right」、「Bottom」、「Left」、「Center」、「Cyan」、「Yellow」、「Pink」と適切な内容に変える。例えば、一番右のトグルボタンを選択し、Properites セクションの Text に Pink と入力する。これで適切な内容が表示された。以上で、このアプリケーションの起動時の見栄えの大事な部分は完成した。

Text の修正

次に、このアプリケーションを稼働させた場合のことを考える。各トグルボタンは、押されていない、押されている、のどちらか一方の状態になり得る。また、3個のトグルボタンをトグルグループに入れるので、あるボタンが押されると他のボタンは押されていない状態に自動的になり(押されているボタンを押し戻すことはできる)、あり得る状態は、どれか1個のボタンが押されている、または、どのボタンも押されていない、の4個の状態である。ユーザーがトグルボタンを押し、背景色を選択したら、どの色が選ばれたかを判断し、Cyan ならば Left と書いてあるラベルの背景色を Cyan に変え、Yellow ならば Bottom と書いてあるラベルの背景色を Yellow に変え、Pink ならば Right と書いてあるラベルの背景色を Pink に変える。また、状況に応じて「Center: どの背景色も選ばれていません。」または、「Center: Cyan が選ばれました。」等と表示する。さて、この動作を SampleFX11Controller.java に記述するのであるが、そのために識別する必要があるものに(自分で)名前を付けておく。必要な名前(id)は、まず、背景色を設定する必要がある labelLeft、labelRight、labelBottom、次に、選ばれた背景色を述べる labelCenter、3個のトグルボタン、toggleButtonCyan、toggleButtonYellow、toggleButtonPink、そして、3個のトグルボタンを1個のグループにする、toggleGroupBGColor である。まず、左右の真ん中にあるContent パネルで left と書いてあるラベルを選び、Inspector パネルの Code セクションの fx:id に その名前 labelLeft を記入する。同様のことを toggleButtonPink まで繰り返す。

名前を付ける

最後に、3個のトグルボタンを1個にまとめるグループに toggleGroupBGColor と名前を付ける。具体的には、まず、Cyan と書かれたトグルボタンを選択し、Inspector パネルの Properties セクションの Toggle Group に toggleGrouptBGColor と記入する。これで toggleGroupBGColor が定義され、toggleButtonCyan がこのグループに入った

トグルグループに入れる(1)

次に、Yellow と書かれたトグルボタンを選択し、Inspector パネルの Properties セクションの Toggle Group の右を見ると、▼ があるのでクリックすると、先ほど定義した toggleGroupBGColor があるのでそれを選ぶ。Pink と書かれたトグルボタンにも同じことを行う。これで3個のトグルボタンがトグルグループ toggleGroupBGColor に入った。

トグルグループに入れる(2)

このアプリケーションの見栄えの細かい部分を修正する。現時点ではこのアプリケーションのウィンドウを大きくすると、それに従って、BorderPane の CENTER が大きくなるが、VBox の中の Center と書かれたラベルと HBox(の中の3個のトグルボタン)は左に寄ったままである。これを修正して、BorderPane の CENTER が大きくなれば、それに従って、CENTER の上部中央の位置に、上述のラベルとHBox(の中の3個のトグルボタン)を移動したい。そのために、左側にある Document パネルの Hierarchy セクションで VBoX と HBox の2個を選択し、右側にある Inspector パネルの Properties セクションの Alignment を TOP_LEFT から TOP_CENTER に変更する

微妙な調整

Scene Builder のメニューから Preview>Show Preview in Window を選ぶと現時点での状況をプレビューできる。下図はこのアプリケーションのウィンドウを横方向に大きくしたところである。3個のトグルボタンと Center と書かれたラベルは左右方向の中央に位置したままである。

プレビュー
プレビュー

以上で、Scene Builder での作業は完了した。メニューから File>Save を選ぶ。Eclipse 内の SampleFX11.fxml も自動的に更新される

SampleFX11.fxml(完成版)

では、SampleFX11Controller.java の記述に移る。このファイルの骨格は Scene Builder が準備してくれている。メニューで View>Show Sample Controller Skelton を選ぶと、Scene Builder が作った骨格が表示される。適宜、右下の Comment と Full をチェックする。左下の Copy を選ぶと、クリップボードへコピーされる。Eclipse の SampleFX11Controller.java を開き、全てを選択し、クリップボードから貼り付ける。

Contoroller Skelton

SampleFX11Controller.java でわれわれが記述しなければならない処理は、

  1. どのトグルボタンが押されたか?または、あるトグルボタンが押し戻され、どのトグルボタンも押されていない状態になったこと、を知ること。
  2. どのトグルボタンが押されたか?を知った場合、それに対応するラベルの背景を該当する背景色で塗り、Center にあるラベルにその旨を書く。どのトグルボタンも押されていない状態になった場合、ラベルの背景色を戻し、Center にあるラベルにその旨を書く。

まず、2つ目に対処する。ラベルの内容を書き換えるには、例えば、

labelCenter.setText("Center: Yellow が選ばれました。");
labelCenter.setText("Center: どの背景色も選ばれていません。");

等と、記述すればよい。背景色に関しては、上記で、JavaFX プロジェクトを作成した時、自動的に作成された CSS ファイル application.css を利用する。背景色を塗る、元に戻す、はこの CSS に背景色を設定するクラスを定義し、そのクラスを該当するラベルに加える、削除する、を行えばよい。例えば、CSS ファイルに

.bgColorYellow {
  -fx-background-color: yellow;
}

と記述し、SampleFX11Controller.java で次のように記述すればよい。

// 背景色を塗る場合
labelBottom.getStyleClass().add("bgColorYellow");
// 背景色を戻す場合
labelBottom.getStyleClass().remove("bgColorYellow");

JavaFX CSS に関しては、JavaFX CSS Reference Guide を参照。完成した application.css は次のようである。

application.css

さて、1つ目の、どのトグルボタンが押されたか?または、どのトグルボタンも押されていない状態になったこと、を知ること、に対処する。まず、旧来の匿名クラスを利用した方法、

toggleButtonCyan.setUserData(0); // 0 means Cyan is selected
toggleButtonYellow.setUserData(1); // 1 means Yellow is selected
toggleButtonPink.setUserData(2); // 2 means Pink is selected
toggleGroupBGColor.selectedToggleProperty().addListener(new ChangeListener<Toggle>() {
  public void changed(ObservableValue<? extends Toggle> ov, Toggle toggle, Toggle new_toggle) {
    if (new_toggle != null) {
      toggleButtonSelected((int)new_toggle.getUserData());
    } else {
      noSelectedToggleButton();
    }
  }
});

または、ラムダ式を利用した、Oracle 社の Java 8 におけるトグルボタンの利用例、JavaFX: Working with JavaFX UI Components の 5 Toggle Button を参考にして

toggleButtonCyan.setUserData(0); // 0 means cyan background color for labelLeft
toggleButtonYellow.setUserData(1); // 1 means yellow background color for labelBottom
toggleButtonPink.setUserData(2); // 2 means pink background color for labelRight
toggleGroupBGColor.selectedToggleProperty().addListener(
	(ObservableValue<? extends Toggle> ov, Toggle toggle, Toggle new_toggle) -> {
		if (new_toggle != null) {
			toggleButtonSelected((int) new_toggle.getUserData());
		} else {
			noSelectedToggleButton();
		}
	}
);

のどちらかを利用すればよい。

各 Cyan、Yellow、Pink トグルボタンにユーザデータとして、自分が選択されたことが分かるように、0、1、2 を設定する。どれかのボタンが選択された、または、どのボタンも選択されていない、状態になった時に、呼び出される、これら3個のトグルボタンをまとめた toggleGroupBGColor の関数 changed() の実際の内容、ここでは、toggleButtonSelected(i) と noSelectedToggleButton() を具体的に記述する。記述すべき内容は、前者は i がどのボタンが選択されたかを示し、それに応じた処理を行う。後者の方はどのボタンも選択されていない場合の処理を行う。結果は次のようになる。以上で、SampleFX11Controller.java も完成した。

SampleFX11Controller.java

次に、アプリケーションウィンドウの初期の大きさを調整しタイトルを加えるために、Main.java を修正する。試行錯誤の上、幅を280、高さを120とした。また、タイトルを SampleFX11 とした。これで Main.java も完成した。

大きさとタイトル

上記で述べたことであるが、このプロジェクト SampleFX11 が自動的に Build され、エラー箇所である赤の波線をポイントし、適切な修正案をクリックすることで、自動で module-info.java が修正された。従って、現時点での module-info.java は次の通りである。

現時点での module-info.java

このプロジェクトの(main 関数のある)Main.java を実行しようとすると、エラーが出るので、それを参考にして、module-info.java を修正し、完成させる。

Package Explorer の プロジェクト SampleFX11 内の Main.java を右クリックし、Run as>Java Application を選ぶ。エラーが発生し、コンソールが表示される。その中に export application to module javafx.graphics とあるので、

実行時のエラー

これを参考にして、module-info.java に次の1行を追加する。

  exports application to javafx.graphics;

同様に、実行しようとするとエラーでコンソールが表示されるので、その内容を参考に、module-info.java を修正する。最終的な完成版は次のようである。

module-info.java(完成版)

ただし、1行目の transitive は、Main.java の start() 関数の定義の所に警告として、The type Stage from module javafx.graphics may not be accessible to clients due to missing ‘requires transitive’ と表示されるので、付け加えた。実行すると、エラーはなく、正常に稼働した。次はウィンドウを少し大きくし、Pink を選んだところである。以上で、SampleFX11 は完成した。

正常に実行された
正常に実行された

最後に、モジュールについての私の理解を述べる。module-info.java の最終的な完成版を参照する。sampleFX11 モジュール宣言の中に、require、requrie transitive、exports … to …、opens … to …、等の命令を記述し、この sampleFX11 モジュールは、どの他のモジュールを利用するか、他のモジュールにこの sampleFX11 モジュール内のどのパッケージの利用を許すか、等を記述する。記述していないものは、利用できない。すなわち、エラーになる。

  requries モジュール名;

これは、sampleFX11 モジュールがモジュール名のモジュールを必要とする、すなわち、sampleFX11 モジュールがモジュール名のモジュールを読み取り、モジュール名のモジュールが sampleFX11 モジュールに読み取られる、ことを許可する。

もし、sampleFX11 モジュールを読みとる別のモジュールがモジュール名のモジュールを読み取る必要がある場合、この別のモジュールが自分で requries を利用するか、または、上記で

requries transitive モジュール名;

とする必要がある。

exports パッケージ名 to コンマで区切られたモジュール名リスト;

これは、sampleFX11 モジュールのパッケージ名のパッケージにモジュール名リストにあるモジュールがアクセスするのを許可する

opens パッケージ名 to コンマで区切られたモジュール名リスト;

これは、実行時に、sampleFX11 モジュールにあるパッケージ名のパッケージをモジュール名リストにあるモジュールがアクセスするのを許可する。