JavaQne 2015
もうずいぶん日時が経ってしまいましたが、1 月 24 日に福岡で行われた JavaQne 2015 に参加してきました。JavaOne じゃなくて、JavaQne です。ちなみに Q は九州の Q です。
きしださんから JavaFX の話をということだったのですが、JavaFX の新しめのところを話してもキョトンとされるだけなのは重々承知しています。なので、比較的食いつきやすい Swing からの移行に関して話をしてきました。
ビックリしたのが Swing 経験者の数。数人いればいい方だと思ってたのですが、半分ぐらいの人が Swing を使っていた経験ありでした。
内容は JJUG CCC 2014 Fall で話したものの焼き直しです。でも、CCC では前半で時間を使いすぎて、後半がかなり駆け足になってしまったので、今回は前半は抑え気味にしました。それでも、最後はちょっと時間が足りなかった ><
それでも、そこそこ反応がよかったのでよしとしてください。
資料はこちら。
前半は JavaFX の紹介と、主な機能について。後半が Swing との差分とマイグレーションという 2 部構成にしました。
前半はおいておいて、後半。
Swing と JavaFX の一番の違いは、なんといっても FXML と CSS だと思っています。
GUI の構造を全部 Java で書かなくてはいけないなんて、ほんと苦痛でしかないです。コードも見づらいので、コードの保守もめんどうです。
Scene Builder でポトペタで FXML を作って、必要に応じて FXML を編集すれば OK。後はイベント処理部分をコントローラクラスに記述するだけ。これだけでかなり楽になります。
JavaFX の CSS はちょっとくせ者で、HTML の CSS と同じだと思っているとすぐにイタい目にあいます。JavaFX の部品のプロパティが分かっていないと書けないんですよね。
どういうプロパティがあるのかは Scnene Builder の CSS Analyzer を使えば分かります。CSS Analyzer はかなり便利でデフォルト値も分かるし、現在適用している値も分かります。
できれば、Scene Builder で CSS ファイルを直接編集できるといいのですが...
次は GUI 部品の話。Swing だとコンポーネント、JavaFX だとコントロールと呼びます。
単純な部品は Swing も JavaFX もだいたい同じです。Button や Label などは、プロパティもほぼ一緒ですし。
ちょっと変わってくるのが、ComboBox やダイアログ。JavaFX のダイアログは Java SE 8u40 から提供されますが、Swing の JOptionPane クラスのようなユーティリティクラスはないので、自分で組み立てます。とはいうものの、自由度は結構高いので、いろいろできます。
全然違うのが、JavaFX のコントロール名の最後に View がつく部品。ListView、TreeView、TableView、TreeTableView の 4 種類です。特に使用頻度が高い TableView については後述します。
レイアウトも Swing と JavaFX では違います。
Swing では、コンテナ + レイアウトマネージャ の組み合わせでレイアウトを行います。これに対して、JavaFX ではコンテナがレイアウト機能を含んでいます。
なので、レイアウトマネージャに対応した何たら Pane を探せば OK。たとえば、FlowLayout であれば FlowPane、BorderLayuot であれば BorderPane など。
実際にレイアウトを組み立てるのは FXML なので、クラス名が分かれば、後は Scene Builder でポトペタできます。
非同期処理は Swing が SwingWorker クラス、JavaFX が Service クラス、Task クラスで表します。
ということで、実際に Swing のサンプルを JavaFX に移植してみました。サンプルは Twitter のツィート検索ツールです。
Swing の GUI の構造から FXML を作成し、それをロードするメインクラスを作成します。次に、FXML に対応して、イベント処理を行うコントローラクラスを作成します。
このサンプルにはテーブルが含まれているのですが、ここが移植の一番めんどうなところです。
Swing の JTable は内部のモデルとして TableModel インタフェースを実装したクラスを保持しています。通常は DefaultTableModel クラスもしくは AbstractTableModel クラスのサブクラスとして実装します。
TableModel インタフェースの一番キモになるメソッドが getValueAt(int row, int column) です。つまり、テーブルモデル内でどのようにデータを保持しているかはおいておいたとしても、テーブルモデルを使う側からすると 2 次元の表の列と行を指定して、その値を取得できるようにすればいいわけです。
これに対して、JavaFX の TableView はモデルとして JavaBeans を使用します。そして、JavaBeans であるモデルクラスのプロパティとテーブルのカラムをバインドさせます。
JTable では単なる 2 次元の表だったのが、TableView ではカラムに意味を持たせるわけです。
そして、そのモデルクラスのリストを TableView に渡してあげると、モデルオブジェクトのプロパティの値を対応するカラムに表示します。
この考え方の違いを理解しないと、TableView 使いにくいになってしまいがち。でも、モデルとしての JavaBeans と、テーブルの対応が分かれば、TableView の方が理解しやすいと思います。
ただ、今の書き方だとカラムとプロパティのバインドがちょっとめんどうなんですよね。
非同期処理もやり方が違うだけで、やろうとしていることは変わりありません。Service/Task の書き方さえ分かれば、SwingWorker からの移行も簡単にできるはずです。
結局、GUI でやることなんてそんなにガラッと変わるわけではありません。GUI 部品を配置して、イベント処理を行うという基本的な考えは同じです。
なので、Swing を使っていたのであれば、JavaFX への移行は結構簡単にできるはずです。