現在 Java で利用されている主な GUI (Graphical User Interface) ライブラリは
AWT、Swing、SWT の 3 つです。ただし AWT のみによる GUI アプリケーション開発は
既にほとんど行われておりません。
-
AWT (Abstract Window Toolkit)
-
初期の Java で標準として使用されていた GUI ライブラリ。当時ウィンドウシステム
ごとに使い方がバラバラだったウィジェットを抽象化し、統一した API で利用できる
ことを目的としていました。Swing が十分熟成された今では AWT のみで GUI を組む
必要性もなくなっていますが、組み込み系では標準で使用されています。
-
Swing (旧JFC; Java Foundation Classes)
-
実行環境依存のウィジェットに頼らず、全てのコンポーネント描画と状態の管理を
Java で実装した GUI ライブラリ。AWT の上に成り立っています。当初は何かと
問題がありましたが現在では機能も速度も安定しています。
-
SWT (Standard Widget Toolkit)
-
JNI を多用することで過去の Swing で問題となっていたグラフィックのボトルネック
を回避した非標準の GUI ライブラリ。しかし今では Swing の高速化によりその優位
性もほとんどなく (一部逆転し)、主に以下のような状況で選択します。
- Swing の Look and Feel よりもっとプラットフォームらしい
ウィジェットが使いたいが AWT では機能が不足している。
- ウィンドウメッセージなど SWT が実装している OS の
ネイティブ機能を利用したい。または開発者が C/C++ スタイルのウィンドウプログラ
ミングに慣れている。
- Eclipse のプラグイン開発やワークベンチを利用したアプリ
ケーション開発。
AWT ではネイティブとほぼ同じ状態でウィジェットが使用されるため、例えば Windows
であれば個々のウィジェットはウィンドウリソースを持ち、OS とメッセージでやり
取りを行っています。Windows で AWT のコンポーネントを調べてみると、それぞれに
ウィンドウハンドルが割り当てられていることが分かります。
一方で Swing は自前で描画処理を行うため、必要な描画領域だけをプラットフォームに
要求します。Swing のウィンドウ調べてみると領域の矩形分にだけウィンドウハンドラが
割り当てられていることが分かります。
Swing のボタンは GUI コンポーネントでありながらプラットフォームのウィンドウ
リソースを消費していません。このようなコンポーネントを Light Weight
Component (軽量コンポーネント) と呼び、逆にリソースが割り当てられている
コンポーネントを Heavy Weight Component (重量コンポーネント) と呼びます。
AWT のコンポーネントは全て重量コンポーネントです。また Swing でも JFrame, JDialog
などのいくつかのコンポーネントは重量コンポーネントです。JMenu は表示領域がフレーム
からはみ出る場合にのみ重量コンポーネントとなります。
Swing はこの軽量化によってウィンドウリソース消費や JNI のオーバーヘッドが軽減され、
また描画がウィンドウメッセージを介さずにダイレクトにグラフィック API へ行く
構成*1になったため、
理論的には「軽く」なりました。
Swing の余談的昔話…
しかしこれはあくまで理論上の話。全ての GUI 処理を Java で実装しなおした Swing は
当初からさまざまな問題を孕んでおり、結果的に AWT より遅くてバグだらけの太った
GUI ライブラリと評価されます。
- 描画処理を Java 側に持ってきた事によりプラットフォームのハードウェアアクセラ
レーションの恩恵がほとんど受けられなくなった。Light Weight 化よりもこの影響の方が
遥かに大きく出てしまったため、結果として著しく速度が劣化した。
- 当時はまだ PC のメモリも少なく Java VM 実装も貧弱なものだったため、Java で多くの
処理を行う事はデメリットでしかなかった。Java に閉じた描画処理と MVC 設計の強制が
当時の Java には「理想的だが無駄なもの」として嫌われた。
- いわゆる「初モノ実装」としてあちこちの挙動が不振だった。
J2SE 1.3 での DirectDraw 対応で幾分マシにはなったものの、Swing のダメダメ感に
見切りを付けた IBM は実用最優先で GUI 周辺を JNI で固めた Eclipse を開発します。
Java の GUI アプリケーションでも十分実用に耐える品質になる事を知らしめた Eclipse
の登場は当時非常にセンセーショナルでした。程なくその GUI 部分 (SWT) だけを利用
してのアプリケーション開発も行われるようになります。
Eclipse の成功は Java GUI のネックが Swing であるという事を如実に表していました。
この SWT の後追いもあり、ハードウェアアクセラレーションへの対応や Windows, X11
などのプラットフォーム依存度を減らすなど GUI 周辺が大幅な改善が行われ、J2SE 1.4
でやっとネイティブアプリケーションと比べても遜色の無い品質に仕上がりました。
しかし現実問題として重量コンポーネントと軽量コンポーネントの差はネイティブの
リソースを割り当てられているかどうかでしかなく、動作速度にまでクリティカルに
影響するほどの要因ではありません (重量/軽量という言葉自体が Swing デビューの
ためのキャッチーな意味合いが強かった)。そもそも重量コンポーネントが遅いなら
プラットフォームのネイティブアプリケーションが全て遅いと言うことになって
しまいますから。
*1 このままグラフィック性能が向上して行けばいつかウィンドウ
メッセージがボトルネックになるだろうと予想されていました。
AWT, Swing, SWT を混在しての利用はあちこちに落とし穴があるので基本的にお勧め
できません。諸事情でどうしてもというのであればそれぞれの特性の違いをよく理解し
十分に注意する必要があります。
Swing は AWT の上に成り立っていますが、両者の混在は軽量コンポーネントと重量
コンポーネントの描画の違いから問題を引き起こす可能性があります。代表的な例は
Swing のメニューが AWT のコンポーネントの下に隠れてしまう問題です。
上記は AWT のテキスト領域が重量コンポーネント (物理的に別のウィジェット)
であるため、軽量コンポーネントである Swing のメニューはその下に描画されて
います。
JMenu の他にも JPopupMenu、JComboBox のドロップダウンリスト、その他意図的に
重ね合わせて使用しているコンポーネントは同じ問題が発生します。また同じ
Swing 間でも重量コンポーネントである JApplet を一般のコンポーネントと同じ
ように使用すると同様の問題が発生します。
ちなみに…
上記の例でウィンドウをもう少し小さくしてメニューがフレームからはみ出るよう
調整すると、メニューが重量コンポーネントに昇格してテキスト領域の上に表示
されます。
AWT-Swing 混在時の問題は重量コンポーネントの上に軽量コンポーネントを描画しない
よう十分注意して設計されているのであれば ─ 例えばフレームやダイアログ単位で
完全に住み分けができているなら問題ありません。
Java 7 では軽量コンポーネントと重量コンポーネントを混在させても問題が起きない
ように改良される予定です。
SWT には AWT との相互利用が可能となるように
SWT_AWT
というクラスが用意されています。しかし両者はイベントキューと EDT (イベント
ディスパッチスレッド) を個別に持っており、一方の EDT から他方の機能を直接実行
しないよう注意が必要です。
SWT_AWT.new_Shell()
を使用することで AWT の Canvas 領域上に SWT の Shell をマッピングし、コンポー
ネントを埋め込むことができます。ただし Canvas は重量コンポーネントであるため
Swing に SWT を埋め込む場合は AWT/Swing の混在と同じメニューの重なりの問題が
発生します。
以下は Swing の JFrame 上で SWT の Browser を使用したサンプルです。
サンプルのソース
Java
final Display display = Display.getDefault();
JFrame frame = new JFrame();
Canvas canvas = new Canvas();
frame.setSize(200, 150);
frame.setLocationRelativeTo(null);
frame.setLayout(new BorderLayout());
frame.add(canvas, BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
frame.addWindowListener(new WindowAdapter(){
@Override
public void windowClosing(WindowEvent e) {
// AWT イベント処理からは SWT の機能を直接実行しない
display.syncExec(new Runnable(){
public void run(){
display.dispose();
}
});
return;
}
});
// Canvas のピアが生成されるまで待機
while(! canvas.isDisplayable()){
try{
Thread.sleep(100);
} catch(InterruptedException ex){/* */}
}
// Canvas の領域に Browser を埋め込み
Shell shell = SWT_AWT.new_Shell(display, canvas);
shell.setLayout(new FillLayout());
Browser browser = new Browser(shell, SWT.NULL);
browser.setUrl("http://www.google.com");
shell.pack();
// SWT のメッセージディスパッチループ
while(! shell.isDisposed()){
if(! display.readAndDispatch()){
display.sleep();
}
}
display.dispose();