はじめに
JavaFXの画面遷移がKotlinで綺麗に書けたので、投稿してみます。
環境
Kotlin 1.1.2
JDK 1.8.0_131
作るもの
ボタンがある2つの画面を用意して、ボタンを押すともう片方の画面に遷移するプログラムを作ります。
条件
fxmlファイル名と対応するコントローラーのKotlinのファイル名が同じで、同一パッケージ内に入っていることが前提です。異なるファイル名やパッケージでやりたい場合には、MainAppクラスのreplacePaneを書き換えて下さい。
Application
class MainApp : Application() {
private lateinit var stage: Stage // replacePaneで参照する
override fun start(primaryStage: Stage) {
stage = primaryStage
replacePane(Page1())
stage.show()
}
fun replacePane(controller: Any) {
val classPath = controller.javaClass.name
val className = controller.javaClass.simpleName
val loader = FXMLLoader(Class.forName(classPath).getResource("$className.fxml"))
.apply { setController(controller) }
val parent = loader.load<Parent>()
stage.title = className
stage.scene = Scene(parent)
// スマートキャストでtransitionをreplacePaneで初期化できる
if (controller is TransitionPane) {
controller.transition = this::replacePane // 関数参照
}
}
}
MainAppのstageのsceneを入れ替えることで画面遷移を実現しています。画面遷移のために呼び出されるメソッドは、replacePaneで遷移先のコントローラーを引数に取ります。
interface TransitionPane {
var transition: ((Any) -> Unit)
}
TransitionPaneは、関数リテラルのプロパティであるtransitionを持つインターフェースです。このインターフェースを、画面遷移させたい(遷移元)コントローラーに実装してあげることで、transitionからreplacePaneを呼べるようにします。replacePaneの中でコントローラーのtransitionがreplacePaneで初期化されています。
fun main(args: Array<String>) {
Application.launch(MainApp::class.java, *args)
}
ちなみに、プログラムの起動はこんな感じで行います。
Controller
class Page1 : Initializable, TransitionPane {
override lateinit var transition: ((Any) -> Unit)
@FXML lateinit var toPage2: Button
override fun initialize(location: URL?, resources: ResourceBundle?) {
toPage2.setOnAction { transition(Page2("Kotlinかわいいよ")) }
}
}
1つ目の画面のコントローラーです。ポイントは、オーバーライドしたtransitionにlateinitをつけることで、MainAppのreplacePaneから初期化できるようにしている所です。
class Page2(val labelText: String) : Initializable, TransitionPane {
override lateinit var transition: ((Any) -> Unit)
@FXML lateinit var toPage1: Button
@FXML lateinit var label: Label
override fun initialize(location: URL?, resources: ResourceBundle?) {
toPage1.setOnAction { transition(Page1()) }
label.text = labelText
}
}
2つめの画面のコントローラーも同じような感じです。fxmlのfx:controllerでコントローラーを指定するのではなく、FXMLLoaderのsetControllerで指定するようにすることで、コントローラーのコンストラクタに引数を設定できるようになります。
FXML
fxmlはFlowPaneにボタンとかを置いただけです。
<FlowPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" >
<children>
<Button fx:id="toPage2" mnemonicParsing="false" text="Page2へ" />
</children>
</FlowPane>
<FlowPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Button fx:id="toPage1" mnemonicParsing="false" text="Page1へ" />
<Label fx:id="label" text="Label">
<FlowPane.margin>
<Insets left="10.0" />
</FlowPane.margin></Label>
</children>
</FlowPane>
おわりに
画面遷移がコンパクトに分かりやすく書けたかと思います。
それにしてもKotlinは書いていて気持ちがいいですね!Kotlinかわいいよ、Kotlin。