第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コンポーネント
ここではButtonをドラッグし、content: [ ]の括弧の中にドロップします(図2 ) 。
図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の実行結果
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 アトリビュートを指定して実行
イベント処理を追加する
では、次に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 クリック後
ここではアクションイベントを変更しましたが、他のイベントはどうでしょう。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によるレイアウト例
このレイアウトはリスト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 スライダとイメージの透明度
では、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 ) 。このスクリプトでは、ウィンドウの上部にスライダ、下部にイメージを表示させます。スライダの値を保持させる変数として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 移動によるイメージの切り替え
しかし、この方法だと、スライダの動きとイメージの移動向きが食い違う部分ができてしまいます。そこで、イメージの移動をスライダに合せるため、放物線を使いました。図14 に使用した放物線を示します。さきほどと同じく、スライダは0から100までの値を取るとしました。また、2枚のイメージが移動するので、もっとも外側に移動した時の移動量はイメージの幅の半分となります。
ここでは、幅が300ピクセルのイメージを使用したので、最大の移動量は150ピクセルになります。そこで、図13 のような放物線にするには、スライダの移動量をx 、イメージの移動量をy とすると、次のように表すことができます。
y = -0.06x 2 + 6x
もう一方のイメージは反対方向に移動するので、上の式の符号を反転させた式で移動量を表すことができます。
図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 イメージが移動することを確かめる
では、次にイメージを入れ替えることにしましょう。イメージを入れ替えるにはイメージの表示順を変更することで実現できます。しかし、イメージと考えるのではなく、コンポーネントの表示順を変化させるということを考えます。
コンポーネントの表示順は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コンポーネントだということはほとんど意識することなく使用できることが、おわかりになったはずです。
次回は、クラスの作成などについて解説する予定です。