JavaFXとウィンドウ¶
- 目次
- JavaFXとウィンドウ
トップレベルウィンドウ¶
JavaFXアプリケーションでは、デフォルトのトップレベルウィンドウが1つ生成されます。
これは、Applicationクラスのstartメソッドの引数で渡されるStageインスタンスです。
ウィンドウの形状¶
JavaFXのStageで表示するトップレベルウィンドウの形状には、次の種類があります。画像は、Windows 10の場合の表示です。
種類 | DECORATED | UNDECORATED | UTILITY | TRANSPARENT |
---|---|---|---|---|
表示例 |
- UNIFIEDはWindows 10での挙動が怪しいので割愛(次のバグチケット参照)
https://bugs.openjdk.java.net/browse/JDK-8154847
デフォルトのDECORATEDは、実行しているデスクトップ環境の標準のウィンドウ・タイトルバーや枠が使われます。
UNDECORATEDは、ウィンドウ・タイトルバーや枠が付きません。
TRANSPARENTは、ウィンドウ・タイトルバーや枠が付かない上に、ウィンドウ内部の描画領域外が透明となっています。
UTILITYは、ウィンドウ・タイトルバーや枠が簡易な(小さな)ものになっています。OS(デスクトップ)によって異なりますが、Windows 10の場合はタイトルバーのアイコン、最小化、最大化ボタンがない形状となっています。
ウィンドウの形状を指定するコード記述例¶
StageクラスのinitStyleメソッドで、引数に列挙型StageStyleの列挙子を指定します。
public void start(Stage primaryStage) {
:
primaryStage.initStyle(StageStyle.UNDECORATED);
:
}
TRANSPARENTを指定する場合、Sceneの塗りつぶし色を透明色に設定します。
Parent root = ...
Scene scene = new Scene(root);
scene.setFill(Color.TRANSPARENT);
rootノードの背景を透明色に設定します。JavaコードまたはFXMLで指定。
root.setStyle("-fx-background-color: transparent");
マウス操作でのウィンドウの位置移動対処(UNDECORATEDおよびTRANSPARENT)¶
ウィンドウの形状を、UNDECORATEDまたはTRANSPARENTにした場合、デスクトップの標準機能でのマウス操作でのウィンドウの位置移動には対応しなくなってしまいます。そこで、自力でマウス操作でウィンドウを移動させるコードを記述する必要があります。
public class HelloApplication extends Application {
private double dragStartX;
private double dragStartY;
:
public void start(Stage primaryStage) {
:
Scene scene = new Scene(...);
// マウスのドラッグ操作でウィンドウを移動
scene.setOnMousePressed(e -> {
dragStartX = e.getSceneX();
dragStartY = e.getSceneY();
});
scene.setOnMouseDragged(e -> {
primaryStage.setX(e.getScreenX() - dragStartX);
primaryStage.setY(e.getScreenY() - dragStartY);
});
:
}
}
マウスのドラッグ操作は、まずMousePressedイベントが発生し、一連のドラッグ中は連続してMouseDraggedイベントが発生します。
MousePressedイベント発生時に、ウィンドウ内でのマウス座標を覚えておきます(getSceneX, getSceneY)。
続くMouseDraggedイベントが来たら、マウスのディスプレイ座標(getScreenX, getScreenY)を基に、MousePressed時のウィンドウ内の座標を減じて、ウィンドウの位置を変更します。
stageのsetX, setYはウィンドウの左上隅の座標を設定するメソッドのため、MousePress時のウィンドウ内の座標を減じておかないと、ドラッグで移動中にマウスカーソルの位置がウィンドウの左上になってしまいます。
ウィンドウの大きさ¶
ルートノードの大きさ(widthとheight)で指定¶
StageやSceneには大きさを指定せず、シーングラフのルートノードに大きさを指定すると、その大きさに基づきウィンドウの大きさが決まります。
Application派生クラスのstartメソッドにおいて、SceneやStageには大きさを明示的に指定しない場合、ルートノードの大きさが収まるウィンドウの大きさとなります。
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
FXMLで画面レイアウトを設定、ルートノードにはprefHeight、prefWidthで大きさを指定。
<AnchorPane id="AnchorPane" prefHeight="480" prefWidth="640" xmlns:fx="http://javafx.com/fxml/1" fx:controller="windowsize.FXMLDocumentController">
この場合、640x480 をコンテンツ領域に持つトップレベルウィンドウが表示されます。通常、コンテンツ領域の大きさに加えて、タイトルバーやウィンドウの枠分大きくなります。
Sceneのコンストラクタで大きさを指定¶
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
- Scene scene = new Scene(root);
+ Scene scene = new Scene(root, 160, 100);
stage.setScene(scene);
stage.show();
}
Sceneのコンストラクタの引数で大きさを指定(ここでは160x100)すると、ルートノード(FXMLで記述したAnchorPane)の大きさではなくSceneの大きさをコンテンツ領域に持つトップレベルウィンドウが表示されます。
Stageのメソッドで大きさを指定¶
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene, 160, 100);
stage.setWidth(320);
stage.setHeight(200);
stage.show();
}
StageのインスタンスにsetWidth、setHeightメソッドで大きさを指定(ここでは320x200)すると、FXMLのルートノードやSceneに大きさではなくStageのインスタンスに指定した大きさのトップレベルウィンドウが表示されます。ウィンドウタイトルや枠を含めた大きさとなります。
ウィンドウの大きさの最小最大¶
トップレベルウィンドウの大きさは、Sceneの大きさに基づき計算された大きさ、Stage自体に指定した大きさの他、ユーザーの操作で変えることができます。
ここで、Stageのインスタンスに大きさの最小・最大を指定しておくと、計算された大きさやユーザーの操作で大きさが変化するときにその最小と最大の大きさを制限することができます。
stage.show();
stage.setMinWidth(320);
stage.setMinHeight(200);
stage.setMaxWidth(1280);
stage.setMaxHeight(768);
ウィンドウのリサイズ¶
リサイズ可否の設定¶
ウィンドウサイズは通常ユーザーにより変更可能です。
これを、リサイズできないよう設定することができます。
primaryStage.setResizable(false); // リサイズ禁止
プロパティでも設定可能です。
primaryStage.resizableProperty().bindBidirectional(
resizableCheckBox.selectedProperty()
);
リサイズ時の再描画・再レイアウト¶
ウィンドウの大きさがユーザーによって変更された場合に、それに応じた表示/レイアウトをし直す必要があります。
以下T.B.D.
シーンの変更に応じてウィンドウのリサイズ¶
シーンの内容を変更して、その変更によりシーンの大きさが変わった際(例えば、あるPaneを隠す)、ウィンドウの大きさをシーンに合わせて変更したい場合は StageのsizeToScene メソッドを呼びます。
ToggleButton showHideButton = ...
void handleShowHide(ActionEvent event) {
: // reconstruct scene
showHideButton.getScene().getWindow().sizeToScene();
}
ウィンドウを最前面へ表示・最背面へ表示¶
最前面に表示させる¶
toFront()のメソッドで、ウィンドウを最前面に表示させることができます。
最背面に表示させる¶
toBack()のメソッドで、ウィンドウを最背面に表示させることができます。
常に最前面に表示させる¶
setAlwaysOnTop(boolean)メソッドで、ウィンドウを常に最前面に表示させることができます。
ウィンドウを閉じる¶
プログラムからウィンドウを閉じる方法¶
- Stageクラスのclose()メソッド
- Platformクラスのexit()メソッド
- StageクラスのfireEventメソッドでWINDOW_CLOSE_REQUESTを渡す
メニュー等からウィンドウを閉じたい(プログラムを終了させたい)場合などに使用します。
ウィンドウをUNDECORATEDやTRANSPARENTにした場合、標準ではウィンドウズを閉じる操作がありませんので、何らかの操作でウィンドウを閉じるUIを用意し、それが操作されたらウィンドウを閉じます。
推奨は3.のfireEventです。1.と2.の方法では後述の setOnCloseRequest で登録したリスナーが呼ばれません。3.の方法であれば呼ばれます。
MenuItem closeItem = ...
closeItem.setOnAction(event -> {
stage.fireEvent(new WindowEvent(stage, WindowEvent.WINDOW_CLOSE_REQUEST));
});
ウィンドウを閉じる操作があった場合に処理を実行する¶
ウィンドウを閉じる操作があった場合に、その操作で処理を実行するには、次のようにリスナーを設定します。
primaryStage.showingProperty().addListener((observable, oldValue, newValue) -> {
if (oldValue == true && newValue == false) {
saveStatus();
}
});
showingPropertyは、ウィンドウが表示されたときにfalse→trueに状態が変化し、登録されたリスナーが呼ばれます。
ウィンドウが閉じられたときに、true→falseに状態が変化し、登録されたリスナーが呼ばれます。
アイコン化(最小化)の際は状態は変化しません。
また、プロセスが終了したとき、Stageのclose()を呼んだときであっても、ウィンドウが閉じられるリスナーが呼ばれます。
setOnCloseRequesetでもウィンドウが閉じるときの処理を実装できますが、いくつか問題が生じます。
primaryStage.setOnCloseRequest(ev -> { ... });
なお、Stageクラスのclose()メソッドを呼んでウィンドウを閉じる場合はこのリスナーは発動しません。また、Platform.exit()メソッドはJavaVMを終了させるので同じくこのリスナーは発動しません。
また、このメソッドを複数回呼び出した場合は、最後の呼び出して登録したリスナーが有効です(メソッド名がsetで始まることから類推できますが、リスナーは上書きされます)。
最小化・最大化¶
最小化¶
setIconifiedメソッドでアイコン化(最小化)することができます。
primaryStage.setIconified(true); // 最小化
最大化¶
setMaximizedメソッドでアイコン化(最小化)することができます。
primaryStage.setMaximized(true); // 最大化
フルスクリーン表示¶
StageのsetFullScreenメソッドでウィンドウを画面全体表示(フルスクリーン表示)することができます。
ウィンドウのタイトルバーへの表示¶
setTitleメソッドでウィンドウのタイトルバーへ文字列を表示することができます。
ウィンドウの可視化/非可視化¶
show()メソッドでウィンドウを可視化します。
hide()メソッドでウィンドウを非可視化します。