2013年7月21日日曜日

Java FX2 を触ってみた。

JavaのGUIといえば、貧弱なSwingの印象が強すぎるのですが、JDK1.7からはJava FX2が非常にプッシュされていまして、こちらはOSネイティブ描画を利用するので、ちょっとしたツールにGUIを付けたいといった場合に非常に便利となりました。

JDK1.7u2ころには、Java FX2を別途インストールするよう求められていたと思うのですが、最近では自動的にバンドルされるようになったようで、インポートするライブラリの場所が変更されていることに注意です(Program Files/Oracle/Java FX 2.0/以下ではなくて、Program Files/Java/jdk1.7.0_XX/jre/libに変更)。そのせいでしばらくは勘違いしてJava FX2.0を弄っていました。

よかった探し

Swingと比較すると記述が非常に楽です。

GUIはXMLで記述しますが、GUIビルダーも用意されています。このGUIビルダーでViewを作り、Controllerクラスを設定すれば、自動的にGUIやアクションがbindされるため、お約束的なコードを書く必要がほとんどありません。

Modelクラスのロジックとの関連付けを、Controllerクラスに少し書くだけでGUIアプリケーションが作れてしまいます。Android Annotationに近い感じでしょうか。

見栄えの変更にCSSを使用できるのも、非常に面白いと思います。.NETのXAMLにしても、AndroidのThemeResourceにしても、標準UIから外れたデザインを作ろうとしたときに、独自記法を学ぶコストが必要になってしまうのに対して、FXMLではWeb標準となっているCSSの知識を流用できます。

イケてないところ

標準UIコンポーネントの貧弱さはSwingから退化してさえいます。FileChooserやDirectoryChooserはOSネイティブのものが利用できるようになったのに、確認警告のダイアログは自作しなければいけないちぐはぐさがよく分かりません。

Labelは単行しか表示できません。改行文字を使って2行に見せること自体はできますが、長い文章を折り返すような処理ができないのです。それでいながらWindowsとMacでそれぞれ起動したときに、Systemフォントの10ptの大きさが違って困りました。これはレイアウトの組み方次第でどうにかなる範疇かもしれませんが…。

最後にデプロイ方法に難を抱えています。JRE7がインストールされている環境はまだ少ないでしょう。Java FX2.2でネイティブデプロイを実現する方法自体は提供されたのですが、その方法というのがパッケージ内にJRE7をまるごと突っ込むという力業で、当然ながらファイルサイズが膨れ上がってしまいます。

微妙なところ

例えば「フェードイン+フレームイン」のアニメーションを実現したいとき、FadeTransitionとTranslateTransitionの複合アニメーションを実行する手段がないので、それぞれのアニメーションをキーフレームアニメーションとして実行することで、擬似的に複合アニメーションを実現しています。

iOSのblocksの記述や、AndroidのAnimationResourceに慣れているのもあると思うのですが、この記述はものすごく気持ち悪い…。

例えば以下は50ミリ秒後(0ミリ秒後だとうまくいかない。原因調査中)にフェードとスライドでテキストを表示して、2000ミリ秒後にフェードアウトさせる、といったコードです。覚えたてで、雑に書いてるのもあるのですが、それにしても無意味に冗長な感があります。

    private Timeline mLastTimeline = null;

    private void showText(String text){
        if(mLastTimeline != null){
            mLastTimeline.stop();
        }
        resultText.setVisible(true);
        resultText.setOpacity(0);
        resultText.setText(text);
        mLastTimeline = new Timeline(
                new KeyFrame(new Duration(50), new EventHandler<ActionEvent>(){
                    @Override
                    public void handle(ActionEvent action) {
                        slideAnimation();
                    }
                }),
                new KeyFrame(new Duration(50),new EventHandler<ActionEvent>(){
                    @Override
                    public void handle(ActionEvent action) {
                        showAnimation();
                    }
                }),
                new KeyFrame(new Duration(2000),new EventHandler<ActionEvent>(){
                    @Override
                    public void handle(ActionEvent action) {
                        hideText();
                    }
                })
                );
        mLastTimeline.setOnFinished(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent arg0) {
                mLastTimeline = null;                
            }
            
        });
        mLastTimeline.play();
    }

    private void slideAnimation(){
        final TranslateTransition translate = new TranslateTransition(new Duration(500));
        translate.setNode(resultText);
        translate.setFromX(100.0);
        translate.setToX(0);
        translate.setInterpolator(Interpolator.EASE_OUT);
        translate.play();
    }

    private void showAnimation(){
        final FadeTransition fadein = new FadeTransition(new Duration(500));
        fadein.setNode(resultText);
        fadein.setFromValue(0.0);
        fadein.setToValue(1.0);
        fadein.play();
    }

    private void hideText(){
        final FadeTransition fadein = new FadeTransition(new Duration(500));
        fadein.setNode(resultText);
        fadein.setFromValue(1.0);
        fadein.setToValue(0.0);
        fadein.setOnFinished(new EventHandler<ActionEvent>(){
            @Override
            public void handle(ActionEvent e) {
                resultText.setText("");
                resultText.setVisible(false);
            }
        });
        fadein.play();
    }

可変長引数のKeyFrameでアニメーションタイムラインを構成する、という方法論自体は悪くないと思いますし、Flashなどに馴染みがあるとむしろとっつきやすいと思います。この辺、JDK1.8のラムダを使えばもう少しマシになるかもしれませんけど…。

同じようなことをiOSでやろうとするとこんな感じかな。
 
self.resultText.text =@"text";
self.resultText.hidden = NO;
self.resultText.alpha = 0.0f;
self.resultText.transform = CGAffinetransformMakeTranslation(1.0f, 0.0f);
[UIView animateWithDuration:0.5f
                 animations:^{
                     self.resultText.alpha = 1.0f;
                     self.resultText.transform = CGAffineTransformIdentity;
                 }
                 completion:^(BOOL finished){
                     //dispatchAfterとかで1.5f秒後にラベルを消す
                     //最初からCoreAnimation.frameworkを使ってキーフレームアニメーションで実現してもいい
                 }

…うん。

結局どうなの

Swing、Java FX1.0の失敗を踏まえつつ、ネイティブの使い心地を実現した、使い勝手のよいUIフレームワークになっているようには思います。Modelロジックに集中することができるため、ちょっとした開発支援ツールを作るのには最適だとすら思っています。

ただし少なくとも国内ではマイナーで終わると思うので、まず業務で使える可能性は低いでしょうし、英語圏の情報をガンガン収集して第一人者になってやるぜ!ってやる気がなければ手を出さない方がいいような気がひしひしとしてます。

次は何に手を出そうかな。

0 件のコメント:

コメントを投稿