ということでさわってみました。
ただ動かすのも面白くない、というかJava面白くない、ということでClojureから呼び出してみました。
JavaFXで私が一番注目していたものはFXML。
XMLは好きじゃないけど、ちゃんとロジックが分離できそうだし、GWTのUiBinderを気に入っていたので楽しみにしていました。
というわけでClojure+FXMLで書いてみました。
プロジェクトの設定(Linux)
Leiningenを使います。
多分Winの場合はそのまま動く。
Linuxの場合は http://www.oracle.com/technetwork/java/javafx/downloads/devpreview-1429449.html から Linux 32-bit をダウンロードして、 lib に jfxrt.jar を、 bin に *.so を置いて下さい。
main.clj
まずはApplicationを継承した起動ポイントを書く。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fx.main | |
[:gen-class | |
:extends javafx.application.Application] | |
[:import | |
[javafx.application Application] | |
[javafx.scene Scene]] | |
[:use | |
[fx.pane :only [root]]]) | |
(defn -start [this stage] | |
(let [scene (Scene. (root))] | |
(doto stage | |
(.setTitle "test") | |
(.setScene scene) | |
(.show)))) | |
(defn -main [& args] | |
(Application/launch fx.main args)) |
main/launchだとエラーが出る謎。
pane.clj
xmlをFXMLLoaderでロードするだけ。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fx.pane | |
[:import | |
[javafx.fxml FXMLLoader]]) | |
(defn root [] | |
(let [fxml (.getResource (class root) "root.xml")] | |
(FXMLLoader/load fxml))) |
root.xml
fx:controllerを指定してButtonを置いてonActionを設定する。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?import javafx.scene.layout.*?> | |
<?import javafx.scene.control.*?> | |
<BorderPane xmlns:fx="http://javafx.com/fxml" | |
fx:controller="fx.controller"> | |
<center> | |
<Button text="Test" onAction="#action" /> | |
</center> | |
</BorderPane> |
controller.clj
Buttonから呼び出される関数を定義する。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns fx.controller | |
[:gen-class | |
:methods [[action [javafx.event.ActionEvent] void]]] | |
[:import | |
[javafx.fxml FXML]]) | |
(defn -action [this e] | |
(let [button (.getSource e)] | |
(.setText button "Action!"))) |
gen-classに:methodsをわざわざ書くのがめんどう。
実行
なんかあっさり動いてしまった。
これでバリバリClojureでGUIが書けるぞー
と思ったらそんなことなかった。
fx:id
Buttonを押したらLabelのテキストが変わるようにしてみたい。
そんな場合はfx:idでControlに名前をつけるとコントローラで扱えるようになる。
その時にコントローラはmutableでFXMLアノテーションが付いたフィールドを持っていなければならない。
gen-classでフィールドは指定できない。
Clojureでは:stateを使うことで状態を実現している。
一番最初に思いついたものはdeftypeを使う方法。
deftype
deftypeではmutableなフィールドを持てます。
なのでdeftypeでコントローラを作ってみます。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(defprotocol Action | |
(action [this e])) | |
(deftype Controller [^{:unsynchronized-mutable true} label] | |
Action | |
(action [this e] | |
(.setText label "Action!"))) |
でもこれだと、コンストラクタが引数をとるのでFXMLLoaderがインスタンスを作れない。
なのでこれを更に継承・・・・・出来なかった。
なぜならdeftypeで作ったクラスはfinalが修飾されている。
メソッドの型も曖昧なままなので継承できたとしても動くかどうかはわからない。
こうなると別の方法を取るしかないですね。
別の方法といってもClojureでclassを作るにはdeftype, defrecord, gen-classしかない。
deftypeは先程の通りコンストラクタが引数をとってしまう。
defrecordではそもそもmutableなフィールドを持てない。
gen-classはフィールドを指定できない。
Javaを使うことを・・・強いられてるんだ!
ここで登場Java!
嫌いじゃないけど使いたくないJava!
Java側でmutableでFXMLアノテーションが付いた変数を宣言しておこうという戦略。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package fx; | |
import javafx.fxml.FXML; | |
import javafx.scene.control.Label; | |
abstract class Controller { | |
@FXML private Label label; | |
} |
これを継承してさあ実行。
javafx.fxml.LoadException: Value is already set.
\(^o^)/
ヤンナルネ・・・・・
結果
onActionで関数を指定して呼び出すことはできたがfx:idが使えなかった。
誰か解決方法があったら教えて下さい!
※追記
javafx.fxml.LoadExceptionは別のところでした・・・
しかし、結果は変わらず。
FXML側で実行すると、エラーが握りつぶされてしまうのですね。
※※追記
解決しました!
0 件のコメント:
コメントを投稿