環境
- Kotlin 1.0.5
- JDK 1.8.0_121
- ControlsFX 8.40.12
MaskerPaneとは
ローディング中であることを表示するクルクルアイコンと、背後のコントロールにアクセスできなくさせるための半透明の覆いがセットになったペインです。JavaFXのProgressIndicatorに覆いを足しただけの機能ですがより手軽に使えます。
Class MaskerPane
今回はJavaFXに用意された同時実行用クラスであるTaskと一緒に使ってみます。
使用例(Taskの場合)
上記キャプチャ画像のコントローラクラスの実装です。
class Controller {
@FXML lateinit private var maskerPane: MaskerPane
@FXML lateinit private var button: Button
@FXML fun onButtonAction(@Suppress("UNUSED_PARAMETER") event: ActionEvent) {
val task = object : Task<String>() {
override fun call(): String {
(0..400).forEach { count ->
updateProgress(count.toDouble(), 400.0) // [1]
Thread.sleep(10)
}
return "done"
}
}
maskerPane.progressProperty().bind(task.progressProperty()) // [2]
maskerPane.visibleProperty().bind(task.runningProperty()) // [3]
Thread(task).start()
}
}
1.JavaFXのTaskには void updateProgress(double workDone, double max) というメソッドが用意されています。作業中はworkDoneを増やしながらmaxになるまでこのメソッドを呼んであげる必要があります。
2.MaskerPaneのprogressをTaskのprogressに追従させます。
task.progressが変更される -> maskerPane.progressが追従する -> maskerPaneの見た目が変わる
といった流れをこれで実現できます。progress自体は0.0から1.0に変化していくDouble値です。
3.MaskerPaneの表示可否をTaskの実行中かどうかを示す値に追従させます。
つまりTaskが動作を停止するとMaskerPaneは消えます。
バリエーション
Webページの読み込みなど、完了のタイミングを予測できない場合があるかと思います。そのような場合は[1]と[2]の行を削除して以下のような表示にできます。
同期周り
javafx.concurrentパッケージはこれらの処理をスレッドセーフに行ってくれます。今回の例ではTaskがprogressやrunningの値を書き換えるときに、自動的にJavaFXアプリケーション・スレッドを使用するようになっているので、画面がごりごり書き換わっていても描画スレッドを意識しなくて済みます。
ORACLE: JavaFXでの同時実行
参考
ControlsFX: Getting Started (sample)
ORACLE: Java Sample - Ensemble
FXML
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.StackPane?>
<?import org.controlsfx.control.MaskerPane?>
<StackPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0"
prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1"
fx:controller="maskerpane.Controller">
<Button fx:id="button" mnemonicParsing="false" onAction="#onButtonAction" text="Button"/>
<MaskerPane fx:id="maskerPane" visible="false"/>
</StackPane>