ついにベールを脱いだJavaFX

第6回Swingのコンポーネント

第4回でシェイプを扱いましたが、シェイプだけでGUIを作るのは片手落ちです。やはり、ボタンやメニュー、テーブルなどといったコンポーネントがないとGUIを構築するのは難しいですね。

第1回でJavaFXはSwingとJava2Dをベースに作られているということを述べました。そのため、このSwingのUIコンポーネントをJavaFXからでも使用できるのです。

とはいうものの、現状ではすべてのSwingコンポーネントを使えるわけではありません。特にメニューやテーブルが使えないのが痛いところです。すでにJavaFXはアップデートリリースされ、JavaFX 1.0 update 1になっています。update 1ではSwingコンポーネントの拡充は行われませんでしたが、今後拡充されていくと筆者は予想しています。

ということで、今回はJavaFXでSwingのコンポーネントを使用する方法について紹介していきます。なお、今回紹介するサンプルコードは以下よりダウンロードできます。

Swingのコンポーネントを使用する

まず、Swingのコンポーネントを単体で使用してみましょう。Swingのコンポーネントだからといって構える必要はありません。第4回のシェイプとほとんど変わりなく使用することができます。

まず、サンプルのためのプロジェクトをNetBeansで作成しましょう。NetBeansでJavaFXのプロジェクトを作成する方法は第4回を参照してください。ここでは、ボタンを例にしていくので、プロジェクト名をbuttonsampleとし、メインファイルは作成しないことにします。

プロジェクトが作成できたら、メインとなるスクリプトファイルを作成します。プロジェクトの一覧が表示されているペインで、buttonsampleを右クリックし、ポップアップメニューを表示させます。そして、メニューの中から[新規⁠⁠→⁠Empty JavaFX File]を選択します。すると、ファイルを作成するダイアログが表示されるので、ファイル名を入力します。ここでは、プロジェクト名と同じbuttonsample.fxとしましょう。

空っぽのファイルが作成されたので、第4回と同じようにエディタの右側にあるコードパレットからStageをドラッグし、エディタ領域にドロップします。するとリスト1のようなコードが作成されます(コメントは省略してあります⁠⁠。

リスト1
import javafx.stage.Stage;
import javafx.scene.Scene;
 
Stage {
    title: "MyApp"
    scene: Scene {
        width: 200
        height: 200
        content: [  ]
    }
}

さあ、これで準備完了です。さっそくSwingのコンポーネントをここに貼っていきましょう。この場合も、コードパレットを使用することができます。Swingのコンポーネントは図1に示したようにSwing Componentsカテゴリにあります。

図1 Swingコンポーネント
図1 Swingコンポーネント

ここではButtonをドラッグし、content: [ ]の括弧の中にドロップします図2⁠。

図2 Buttonをドラッグ&ドロップ
図2 Buttonをドラッグ&ドロップ

Buttonをドロップすると、コードはリスト2のようになります(整形してあります⁠⁠。

リスト2
import javafx.ext.swing.SwingButton;
import javafx.scene.Scene;
import javafx.stage.Stage;
 
Stage {
    title: "MyApp"
    scene: Scene {
        width: 200
        height: 200
        content: [
            SwingButton {
                text: "Button"
                action: function() {
 
                }
            }
        ]
    }
}

ここからわかることは、SwingのボタンはSwingButtonというクラスで表されるということ、またSwingButtonクラスはjavafx.ext.swingパッケージで定義されていることです。

JavaFX ScriptのAPIドキュメントを見るとわかりますが、他のSwingコンポーネントもjavafx.ext.swingパッケージで定義されており、それぞれSwingで始まる名前になっています。

それでは、この状態で実行してみましょう。もちろん、プレビューでもかまいません。実行結果を図3に示しました。

図3 buttonsampleの実行結果
図3 buttonsampleの実行結果

JavaFXをJava SE 6u10以降のバージョンで動作させると、Java SE 6u10で導入された新しいルック&フィールのNimbusが使用されます。そのため、見た目が今までのSwingコンポーネントとはちょっと違っています。最近の傾向であるグラデーションを多用したルック&フィールになっています。

それでは、このスクリプトを進化させていきましょう。デフォルトではtextアトリビュートとactionアトリビュートしか初期化されていないので、その他のアトリビュートを付け加えてみました。SwingButtonクラスのアトリビュートはSwingButtonクラスのAPIドキュメントを参照してください。

第3回で触れたシェイプも、ここで使用しているSwingButtonクラスもjavafx.scene.Nodeクラスのサブクラスです。そのため、シェイプで使用したアトリビュートの多くはSwingButtonクラスでも使用することができます。

リスト3のコードではwidth、height、translateX、translateY、fontの各アトリビュートに値を代入するようにしました。

リスト3
Stage {
    title: "Button Sample"
    scene: Scene {
        width: 140
        height: 100
        content: [
            SwingButton {
                text: "Click!"
                width: 100
                height: 60
                translateX: 20
                translateY: 20
                font: Font {
                    size: 24
                }
                action: function() {

                }
            }
        ]
    }
}

widthアトリビュートはコンポーネントの幅、heightアトリビュートは高さを表します。また、translateXはx軸方向のコンポーネントの移動量、同じくtranslateYはy軸方向の移動量になります。fontはボタンの文字を描画するフォントを指定します。また、ボタンに表示される文字列も変更しました。では、この状態で再び実行させてみましょう図4⁠。

図4 アトリビュートを指定して実行
図4 アトリビュートを指定して実行

イベント処理を追加する

では、次にbuttonsample.fxにボタンがクリックされた時のイベント処理を追加していきます。といっても、すでにactionというアトリビュートがあるのが見え見えですね。actionアトリビュートは関数型のアトリビュートで、関数を代入します。ここで指定する関数は、JavaのActionListenerインターフェースのactionPerformedメソッドに相当します。

ここでは、クリックされたら表示する文字列を変化させることにしましょう。ここで、使うのが第1回で説明したバインドです。

バインドは変数やアトリビュートを自動的に同期させる機構です。この例ではボタンに表示する文字列を保持する変数を作成し、ボタンのtextアトリビュートはその変数にバインドするようにします。こうすることで、文字列が変更されると、自動的にボタンの文字列も更新されますリスト4⁠。

リスト4 
// ボタンに表示する文字列
var buttonText = "Click!";
 
Stage {
    title: "Button Sample"
    scene: Scene {
        width: 280
        height: 100
        content: [
            SwingButton {
                // 表示文字列は buttonText にバインドする
                text: bind buttonText
                width: 240
                height: 60
                translateX: 20
                translateY: 20
                font: Font {
                    size: 24
                }
                action: function() {
                    // クリックされたらbuttonTextを更新する
                    buttonText = "One More Click!"
                }
            }
        ]
    }
}

変数buttonTextがボタンに表示する文字列です(青字部分⁠⁠。実際にボタンに表示するのはSwingButtonクラスのtextアトリビュートですが、ここでは赤字で示したようにtextアトリビュートを変数buttonTextにバインドしています。そして、クリックされた時にコールされるactionアトリビュートには、オレンジで示したように変数buttonTextを変更する関数を代入します。

JavaではActionListenerインターフェースをインプリメントしたクラスを作成して、addActionListenerメソッドで登録しますが、それに比べると格段に簡単にイベント処理が行えることがおわかりのはずです。

これで実行してみましょう。なお、このソースは日本語を使用しているため、第2回の4ページで示した文字コードの変更をしておかないとコンパイラエラーが発生してしまいます。

実行すると、一度もクリックしていないと図5の状態にありますが、クリックすると図6のように変化します。

図5 クリック前と 図6 クリック後
図5 クリック前 図6 クリック後

ここではアクションイベントを変更しましたが、他のイベントはどうでしょう。SwingButtonのスーパークラスのNodeクラスにはactionアトリビュートの他に、onMouseClickedアトリビュートやonKeyPressedアトリビュートなど一揃いのイベントに対するアトリビュートが揃っています。これらのアトリビュートも使ってみましょうリスト5⁠。

リスト5
// ボタンに表示する文字列
var buttonText = "Initial";
 
Stage {
    title: "Button Sample"
    scene: Scene {
        width: 240
        height: 100
        content: [
                SwingButton {
                // 表示文字列は buttonText にバインドする
                text: bind buttonText
                width: 200
                height: 60
                translateX: 20
                translateY: 20
                font: Font {
                    size: 24
                }
                
                onMouseEntered: function(event: MouseEvent) {
                    buttonText = "Enter"
                }
                onMouseExited: function(event: MouseEvent) {
                    buttonText = "Exit"
                }
                onMousePressed: function(event: MouseEvent) {
                    buttonText = "Press"
                }
                onMouseReleased: function(event: MouseEvent) {
                    buttonText = "Release"
                }
            }

        ]

    }
}

actionアトリビュートは引数のない関数でしたが、onMousePressedアトリビュートなどはjavafx.scene.input.MouseEventオブジェクトが引数となります。ここでは、引数のeventの型を明記しましたが、省略することもできます。

図7をクリックすると完成したbuttonsample.fxをアプレットとして実行するページに移動しますので、ぜひ試してみてください。なお、このアプレットはJava SE 6u10で提供された新しいJava Plug-in上でしか動作しませんので、ご注意ください。

図7 完成したbuttonsample.fx

レイアウト

今までのサンプルのように、1つのコンポーネントから構成されるGUIというのはほとんどありません。通常は複数のコンポーネントを組み合わせてGUIを構築します。その時に問題になるのが、コンポーネントのレイアウトです。

レイアウトも現状のJavaFX Scriptでは弱いところです。インタープリタ版ではJavaのGroupLayoutクラスに対応するレイアウトマネージャも提供されていたのですが、JavaFX 1.0では使えなくなってしまいました。現状提供されているのは、HBoxクラスとVBoxクラスです。この2つのクラスはJavaのBoxLayoutクラスに相当します。

この2つのクラスはその名の通り、箱です。HBoxクラスが水平方向の箱、VBoxクラスが垂直方向の箱です。この箱にコンポーネントを並べていくことで、レイアウトを行うわけです。ここで作成するサンプルは図8のようにテキストフィールド、ボタン、ラベルから構成されます。テキストフィールドとボタンをHBoxオブジェクトに水平方向に並べます。VBoxオブジェクトにはテキストフィールドとボタンが並べられたHBoxオブジェクトとラベルを垂直方向に並べます。

図8 HBox, VBoxによるレイアウト例
図8 HBox, VBoxによるレイアウト例

このレイアウトはリスト6のようなコードで実現できます。

リスト6
VBox {
    content: [
        HBox {
            content: [
                SwingTextField { ... },
                SwingButton { ... }
            ]
        },
        SwingLabel { ... }
    ]
}

Javaではコンポーネントをレイアウトマネージャに登録するのに何度もaddメソッドをコールしなければいけなかったのですが、シーケンスに並べるだけなので簡潔に表すことができます。とはいうものの、複雑なレイアウトはHBoxクラス、VBoxクラスでは難しいので、早くさまざまなレイアウトマネージャが使えるようになってほしいですね。

では、ここでプロジェクトを作成して、サンプルを動作させてみましょう。プロジェクト名はsayhello、メインスクリプトファイルはsayhello.fxとしました。

ではsayhello.fxを見ていきましょう。このスクリプトでは2つの変数を使用しています。リスト7にこの変数と関連するコンポーネントの部分だけ抽出してみました。

リスト7
var who = "World";
var label = "Hello, World!";
 
Stage {
    scene: Scene {
        content: [
            SwingTextField {
                text: bind who with inverse
            },
            SwingButton {
                action: function() {
                    label = "Hello, {who}!"
                }
            },
            SwingLabel {
                text: bind label
            }
        ]
    }
}

テキストフィールドに表示している文字列は変数whoにバインドしてあります。しかし、テキストフィールドに表示している文字列は編集されることがあります。そこで、バインドにwith inverseを付加することで、双方向のバインドにしました。こうすることで、SwingTextFieldオブジェクトのtextアトリビュートが更新した時に、whoも自動的に更新します。

そして、ラベルに表示する文字列がlabelです。こちらは普通のバインドです。labelはボタンが押された時に更新します。

実行結果を図9に示します。この図もアプレットページへのリンクになっています。

図9 完成したbuttonsample.fx(左図)とsayhelloの実行結果(右図)

ラベルに表示する文字を、リスト8のように直接whoにバインドすることもできます。

リスト8
var who = "World";

Stage {
    scene: Scene {
        content: [
            SwingTextField {
                text: bind who with inverse
            },
            SwingButton {
                action: function() {
//                    label = "Hello, {who}!"
                }
            },
            SwingLabel {
                text: bind "Hello, {who}!"
            }
        ]
    }
}

しかし、こうしてしまうとテキストフィールドで編集すると、即座にラベルも更新してしまいます。そのため、ボタンの意味がなくなってしまうのです。

念のため、スクリプトの全文をリスト9に示しておきます。

リスト9
import javafx.ext.swing.SwingButton;
import javafx.ext.swing.SwingLabel;
import javafx.ext.swing.SwingTextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.scene.Scene;
import javafx.scene.text.Font;
import javafx.stage.Stage;
 
var who = "World";
var label = "Hello, World!";
 
Stage {
    title: "Say Hello to..."
    scene: Scene {
        width: 220
        height: 100
        content: [
            VBox {
                translateX: 10
                translateY: 10
                spacing: 10
                content: [
                    HBox {
                        spacing: 10
                        content: [
                            SwingTextField {
                                columns: 10
                                text: bind who with inverse
                                editable: true
                            },
                            SwingButton {
                                text: "Update"
                                action: function() {
                                    label = "Hello, {who}!"
                                }
                            }
                        ]
                    },
                    SwingLabel {
                        font: Font {
                            size: 24
                        }
                        width:200
                        text: bind label
                    }
                ]
            }
        ]
    }
}

他のノードとSwingコンポーネントを組み合わせる

今までのサンプルで、Swingのコンポーネントがシェイプなどのノードとまったく同じように扱えることがわかりました。では、同じアプリケーションの中で他のノードとSwingコンポーネントを一緒に表示することはできるのでしょうか。

答えは、もちろん可能です。

SwingコンポーネントもNodeクラスのサブクラスですから、全く区別なく使用することができます。これもサンプルで試してみましょう。ここで作成するのは2枚のイメージをスライダで切り替えるというアプリケーションです。スライダがSwingコンポーネントです。

図10に示したようにイメージの透明度をスライダで変更して、イメージを切り替えます。

図10 スライダとイメージの透明度
図10 スライダとイメージの透明度

では、slideimagesというプロジェクトを作成し、slideimages.fxファイルを作成していきましょう。

アプリケーションの全体を作る前に、イメージを扱う方法について説明しておきます。JavaFX Scriptでイメージを扱うためのクラスはjavafx.scene.image.ImageViewクラスです。NetBeansのコードパレットではBasic Shapesの中にImageとして配置されています。コードパレットからエディタにドラッグ&ドロップすると、リスト10のようなコードが生成されます。

リスト10
ImageView {
    image: Image {
        url: "{__DIR__}/myPicture.png"
    }
}

このコードを見ると、ImageViewクラスがimageアトリビュートを持っていることがわかります。このimageアトリビュートの型はjavafx.scene.image.Imageクラスです。

同じような名前のImageViewクラスとImageクラスですが、役割は異なります。Imageクラスはイメージをロードし、イメージのピクセル情報を保持します。一方のImageViewクラスはイメージを表示するためのGUIコンポーネントになります。シェイプなどと同様にImageViewクラスもNodeクラスのサブクラスです。

ImageクラスのurlアトリビュートはイメージをロードするURLを表しています。赤字で示した__DIR__は、このスクリプトがコンパイルされたクラスファイルがある場所を指しています。スクリプトをローカルで実行する時も、アプレットやJava Web Startのようにリモートからダウンロードして使用する時も、適切なURLを作成してくれるので、とても便利です。

ここでは、herme.jpgとaoki.jpgという2枚のイメージを使いました。ちょうどクリスマスということなので、イメージもクリスマスケーキです。このイメージをスクリプトファイルと同じディレクトリに配置します。Windowsの場合、NetBeansも使えばエクスプローラから直接ドラッグ&ドロップできます図11⁠。

図11 イメージをドラッグ&ドロップ
図11 イメージをドラッ

では、スクリプトを作っていきましょうリスト11⁠。このスクリプトでは、ウィンドウの上部にスライダ、下部にイメージを表示させます。スライダの値を保持させる変数としてsliderValueを作成しました。Nodeクラスには不透明度をあらわすopacityアトリビュートがあるので、これをsliderValueとバインドさせます。

リスト11
// スライダの値
var sliderValue = 0;
 
Stage {
    title: "Slide Images"
    scene: Scene {
        width: 300
        height: 500
        content: [
            SwingSlider {
                translateX: 20
                translateY: 15
                width: 260
                minimum: 0
                maximum: 100
                value: bind sliderValue with inverse
                vertical: false
            },
            // 2 枚のイメージで透明度を変化させる
            ImageView {
                x: 0
                y: 50
                opacity: bind sliderValue / 100.0
                image: Image {
                    url: "{__DIR__}herme.jpg"
                }
            },
            ImageView {
                x: 0
                y: 50
                opacity: bind 1.0 - sliderValue / 100.0
                image: Image {
                    url: "{__DIR__}aoki.jpg"
                }
            }
        ]
    }
}

スライダを動かしたときに変数sliderValueも同時に更新されるように、先ほどと同じく双方向バインドにしました(赤字部分)。SwingSliderクラスはvalueがIntegerなので、sliderValueもIntegerです。しかし、Nodeクラスのopacityアトリビュートは型がNumberであり、0から1までの値をとります。そこで、sliderValueを1/100にしてバインドさせています。

青字で示したように、一方のイメージのopacityが大きいときは、もう一方のopacityが小さくなるように1 - sliderValue/100としています。

図12にslideimagesの実行結果を示します。イメージが重なっていることがわかるはずです。また、今までのサンプルと同じように図12をクリックすると、slideimageをアプレットとして実行させるページに移動します。

図12 slideimages

置換トリガを使う

もう1つ同じようなイメージの切り替えを行ってみましょう。こんどはイメージを移動させて、切り替えを行います。

重なったイメージを同時に左右に移動させ、重ならなくなったところでイメージの重なり順を変更します。そして、再び中央に向かって移動させることでイメージが切り替わります図13⁠。

図13 移動によるイメージの切り替え
図13 移動によるイメージの切り替え

しかし、この方法だと、スライダの動きとイメージの移動向きが食い違う部分ができてしまいます。そこで、イメージの移動をスライダに合せるため、放物線を使いました。図14に使用した放物線を示します。さきほどと同じく、スライダは0から100までの値を取るとしました。また、2枚のイメージが移動するので、もっとも外側に移動した時の移動量はイメージの幅の半分となります。

ここでは、幅が300ピクセルのイメージを使用したので、最大の移動量は150ピクセルになります。そこで、図13のような放物線にするには、スライダの移動量をxイメージの移動量をyとすると、次のように表すことができます。

y = -0.06x2 + 6x

もう一方のイメージは反対方向に移動するので、上の式の符号を反転させた式で移動量を表すことができます。

図14 スライダ移動量とイメージ移動量
図14 スライダ移動量とイメージ移動量

では、スクリプトを書いていきましょうリスト12⁠。ここではプロジェクト名をslideimage2とし、スクリプト名はslideimage2.fxとしました。

とりあえず、イメージの入れ替えは考えずに、スライダと同期してイメージが移動するスクリプトを作成します。先ほどのslideimage.fxと構造はほとんど同じなので、簡単ですね。異なるのはopacityアトリビュートをスライダ移動量と同期させるのではなく、x軸方向の移動量を示すtranslateXアトリビュートにバインドさせることです。

リスト12
// スライダの値にバインドした変数
var sliderValue = 0;
 
// スライダの値に応じた移動量
var transX = bind sliderValue * (6.0 - 3.0 * sliderValue / 50.0);
 
Stage {
    title: "Slide Images"
    scene: Scene {
        width: 300
        height: 500
        content: [
            SwingSlider {
                translateX: 20
                translateY: 15
                width: 260
                minimum: 0
                maximum: 100
                value: bind sliderValue with inverse
                vertical: false
            },
            ImageView {
                x: 0
                y: 50


                translateX: bind transX
                image: Image {
                    url: "{__DIR__}herme.jpg"
                }
            },
            ImageView {
                x: 0
                y: 50
                translateX: bind -transX
                image: Image {
                    url: "{__DIR__}aoki.jpg"
                }
            }
        ]
    }
}

赤字で示した式が放物線の数式に対応します。それぞれのイメージのtranslateXアトリビュートは青字で示したように、赤字で求めたtransXにバインドさせます。

この段階で一度実行させてみましょう。イメージは移動するものの、図15に示した通り、スライダを移動させてもイメージは入れ替わりません。

図15 イメージが移動することを確かめる
図15 イメージが移動することを確かめる

では、次にイメージを入れ替えることにしましょう。イメージを入れ替えるにはイメージの表示順を変更することで実現できます。しかし、イメージと考えるのではなく、コンポーネントの表示順を変化させるということを考えます。

コンポーネントの表示順はSceneオブジェクトのcontentアトリビュートの要素順によって決まります。contentアトリビュートはシーケンスであり、インデックスの小さい要素ほど先に描画されます。

現在のcontentアトリビュートは以下のようになっています。

content: [スライダ, チョコケーキ, イチゴケーキ]

つまり、イチゴケーキのイメージが一番最後に描画されます。イメージを入れ替えるにはcontentアトリビュートの要素を次の順番にすればいいことがわかります。

content: [スライダ, イチゴケーキ, チョコケーキ]

順番を入れ替えるにはシーケンスに対するreverse演算子を適用すればいいのですが、スライダがちょっと邪魔です。そこで、イメージだけ別にしたシーケンスを用意して、そのシーケンスに対してreverse演算子を適用させるようにします。手続き風に書けば、リスト13のような感じになります。

リスト13
var images = [チョコケーキ, イチゴケーキ]

content: [スライダ, images]

if (sliderValue == 50) {
	images = reverse images;
}

ここで思い出していただきたいのが、JavaFX Scriptではシーケンスは1次元しかなく、シーケンスの中にシーケンスを記述すると展開されてしまうということです。したがって、上記のように記述すると、contentは結果的に[スライダ, チョコケーキ, イチゴケーキ]になります。

順番を入れ替えるのはimagesだけなので、contentアトリビュートでのスライダの順序は変わらず、イメージだけ順序を変えられるわけです。実際にはimagesを書き換えてしまっているので、バインドすることが必要です。

問題は上記のコードのif文をどこに書くかということです。ここで使えるのが、第1回の6ページで解説した置換トリガです。

置換トリガーを使用すれば、変数もしくはアトリビュートの値が変更した場合、決められた処理を行うことができます。ここでは、変数sliderValueに置換トリガを設定し、sliderValueの値が50の時にシーケンスの順序を入れ替えるようにします。置換トリガを使用するようにしたスクリプトをリスト14に示します。

リスト14
// イメージのシーケンス
// 未定義でエラーにならないよう、先に定義だけ行う
var images: ImageView[] = [];
 
// スライダの値にバインドした変数
// スライダの位置が半分に来た時にイメージの並び順を変更する
var sliderValue = 0 on replace {
    if (sliderValue == 50) {
        images = reverse images;
    }
};
      
// スライダの値に応じた移動量
var transX = bind sliderValue * (6.0 - 6.0 * sliderValue / 100.0);
 
// 2 枚のイメージ
images = [
    ImageView {
        x: 0
        y: 50
        translateX: bind transX
        image: Image {
            url: "{__DIR__}herme.jpg"
        }
    },
    ImageView {
        x: 0
        y: 50
        translateX: bind -transX
        image: Image {
            url: "{__DIR__}aoki.jpg"
        }
    }
];
 
Stage {
    title: "Slide Images"
    scene: Scene {
        width: 300
        height: 500
        // バインドするのは images の変更に同期するため
        content: bind [
            SwingSlider {
                translateX: 20
                translateY: 15
                width: 260
                minimum: 0
                maximum: 100
                value: bind sliderValue with inverse
                vertical: false
            },
            images
        ]
    }
}

赤字で示したように、置換トリガはon replace { ... }で表します。括弧内の処理はsliderValueが変化するたびにコールされます。そこで、sliderValueが50の時に変数imagesを反転させています。

変数imagesは変数sliderValueの定義より前に初期化もできればいいのですが、変数transXに依存しているため、sliderValueより先に初期化することができません。そのため、まず変数imagesの定義だけ行い、値を代入するのはtransXを定義した後にしています。

また、変数imagesは置換トリガ内部で値が変化しています。そのため、変数imagesの変更に同期させるため、Sceneオブジェクトのcontentアトリビュートとバインドさせています(青字部分⁠⁠。

では、実行してみましょう。図16に実行結果を示します。スライダの位置は図15と同じぐらいのところにありますが、チョコケーキのイメージの方が上に来ていることがわります。このイメージもアプレットページへのリンクになっていますので、ぜひ試してみてください。

図16 完成したslideimages2

Swingコンポーネントはノード

最後にちょっとしたおまけ的なものを紹介しましょう。Swingコンポーネントはシェイプなどのノードと同等に扱えると何度も書いてきました。では、リスト15のスクリプトを試してみてください。

リスト15
// スライダの値
var sliderValue = 0;

Stage {
    title: "Rotate Field"
    scene: Scene {
        width: 300
        height: 250
        content: [
            SwingSlider {
                translateX: 20
                translateY: 15
                width: 260
                minimum: -180
                maximum: 180
                value: bind sliderValue with inverse
                vertical: false
            },
            SwingTextField {
                translateX: 50
                translateY: 120
                width: 200
                height: 50
                // テキストフィールドを回転させる
                rotate: bind sliderValue
                columns: 10
                font: Font {
                    size: 24
                }
                text: "TextField"
                editable: true
            }
        ]

    }
}

このスクリプトを実行すると、スライダとテキストフィールドを表示します。では、スライダを移動させてみてください図17⁠。こんな状態になっっても、ちゃんとテキストフィールドとして使用することができるのです。

このようなことができると、今までSwingで作成していたときには思いもつかなかった斬新なUIのアイディアが沸いてくるかもしれませんね。

図17 回転するテキストフィールド

今回は、Swingのコンポーネントを使用することについて解説を行いました。Swingコンポーネントだということはほとんど意識することなく使用できることが、おわかりになったはずです。

次回は、クラスの作成などについて解説する予定です。

おすすめ記事

記事・ニュース一覧