Java GUI

2008年03月22日

Java で使用できる GUI

現在 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 の高速化によりその優位性もほとんどなく (一部逆転し)、主に以下のような状況で選択します。

  1. Swing の Look and Feel よりもっとプラットフォームらしいウィジェットが使いたいが AWT では機能が不足している。
  2. ウィンドウメッセージなど SWT が実装している OS のネイティブ機能を利用したい。または開発者が C/C++ スタイルのウィンドウプログラミングに慣れている。
  3. Eclipse のプラグイン開発やワークベンチを利用したアプリケーション開発。

軽量コンポーネント vs 重量コンポーネント

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 の余談的昔話…

しかし現実問題として重量コンポーネントと軽量コンポーネントの差はネイティブのリソースを割り当てられているかどうかでしかなく、動作速度にまでクリティカルに影響するほどの要因ではありません (重量/軽量という言葉自体が Swing デビューのためのキャッチーな意味合いが強かった)。そもそも重量コンポーネントが遅いならプラットフォームのネイティブアプリケーションが全て遅いと言うことになってしまいますから。

*1 このままグラフィック性能が向上して行けばいつかウィンドウメッセージがボトルネックになるだろうと予想されていました。

ウィジェットの混在方法

AWT, Swing, SWT を混在しての利用はあちこちに落とし穴があるので基本的にお勧めできません。諸事情でどうしてもというのであればそれぞれの特性の違いをよく理解し十分に注意する必要があります。

AWT-Swing 混在の注意点

Swing は AWT の上に成り立っていますが、両者の混在は軽量コンポーネントと重量コンポーネントの描画の違いから問題を引き起こす可能性があります。代表的な例は Swing のメニューが AWT のコンポーネントの下に隠れてしまう問題です。

Heavy Weight の下に隠れたメニュー Heavy Weight の下に隠れたメニュー

上記は AWT のテキスト領域が重量コンポーネント (物理的に別のウィジェット) であるため、軽量コンポーネントである Swing のメニューはその下に描画されています。

JMenu の他にも JPopupMenu、JComboBox のドロップダウンリスト、その他意図的に重ね合わせて使用しているコンポーネントは同じ問題が発生します。また同じ Swing 間でも重量コンポーネントである JApplet を一般のコンポーネントと同じように使用すると同様の問題が発生します。

+ちなみに…

AWT-Swing 混在時の問題は重量コンポーネントの上に軽量コンポーネントを描画しないよう十分注意して設計されているのであれば─例えばフレームやダイアログ単位で完全に住み分けができているなら問題ありません。

Java 7 では軽量コンポーネントと重量コンポーネントを混在させても問題が起きないように改良される予定です。

SWT-AWT/Swing の混在

SWT には AWT との相互利用が可能となるように SWT_AWTJava™ API リファレンス というクラスが用意されています。しかし両者はイベントキューと EDT (イベントディスパッチスレッド) を個別に持っており、一方の EDT から他方の機能を直接実行しないよう注意が必要です。

SWT_AWT.new_Shell()Java™ API リファレンス を使用することで AWT の Canvas 領域上に SWT の Shell をマッピングし、コンポーネントを埋め込むことができます。ただし Canvas は重量コンポーネントであるため Swing に SWT を埋め込む場合は AWT/Swing の混在と同じメニューの重なりの問題が発生します。

以下は Swing の JFrame 上で SWT の Browser を使用したサンプルです。

+サンプルのソース
Swing で Browser を表示

GUI コンポーネントの対応

AWT Swing SWT
基本 カスタム
一般ウィンドウ FrameJava™ API リファレンス JFrameJava™ API リファレンス ShellJava™ API リファレンス
一般ダイアログ DialogJava™ API リファレンス JDialogJava™ API リファレンス ShellJava™ API リファレンス
ファイル選択ダイアログ FileDialogJava™ API リファレンス JFileChooserJava™ API リファレンス FileDialogJava™ API リファレンス
DirectoryDialogJava™ API リファレンス
フォント選択ダイアログ - - FontDialogJava™ API リファレンス
色選択ダイアログ - JColorChooserJava™ API リファレンス ColorDialogJava™ API リファレンス
メッセージダイアログ - JOptionPaneJava™ API リファレンス MessageDialogJava™ API リファレンス
ラベル LabelJava™ API リファレンス JLabelJava™ API リファレンス LabelJava™ API リファレンス
テキストフィールド TextFieldJava™ API リファレンス JTextFieldJava™ API リファレンス TextJava™ API リファレンス
テキスト領域 TextAreaJava™ API リファレンス JTextAreaJava™ API リファレンス TextJava™ API リファレンス
パスワード入力 FieldJava™ API リファレンス JPasswordFieldJava™ API リファレンス TextJava™ API リファレンス
日時書式 - JFormattedTextFieldJava™ API リファレンス DateTimeJava™ API リファレンス
セパレータ - JSeparatorJava™ API リファレンス -
通常ボタン ButtonJava™ API リファレンス JButtonJava™ API リファレンス ButtonJava™ API リファレンス
トグルボタン - JToggleButtonJava™ API リファレンス ButtonJava™ API リファレンス
ドロップダウンリスト ChoiceJava™ API リファレンス JComboBoxJava™ API リファレンス ComboJava™ API リファレンス
リスト ListJava™ API リファレンス JListJava™ API リファレンス ComboJava™ API リファレンス
ListJava™ API リファレンス
チェックボックス CheckboxJava™ API リファレンス JCheckBoxJava™ API リファレンス ButtonJava™ API リファレンス
ラジオ CheckboxJava™ API リファレンス JCheckBoxJava™ API リファレンス ButtonJava™ API リファレンス
プログレスバー - JProgressBarJava™ API リファレンス ProgressBarJava™ API リファレンス
スライダー - JSliderJava™ API リファレンス ScaleJava™ API リファレンス
SliderJava™ API リファレンス
スピナー - JSpinnerJava™ API リファレンス SpinnerJava™ API リファレンス
リンク - - LinkJava™ API リファレンス
テーブル - JTableJava™ API リファレンス TableJava™ API リファレンス
ツリー - JTreeJava™ API リファレンス TreeJava™ API リファレンス
HTML表示 - JEditorPaneJava™ API リファレンス
JTextPaneJava™ API リファレンス
BrowserJava™ API リファレンス
フローティング - JToolBarJava™ API リファレンス CoolBarJava™ API リファレンス
ToolBarJava™ API リファレンス
メニューバー MenuBarJava™ API リファレンス JMenuBarJava™ API リファレンス MenuJava™ API リファレンス
メニュー MenuJava™ API リファレンス JMenuJava™ API リファレンス MenuJava™ API リファレンス
メニュー項目 MenuItemJava™ API リファレンス JMenuItemJava™ API リファレンス MenuItemJava™ API リファレンス
チェックメニュー項目 CheckboxMenuItemJava™ API リファレンス JCheckBoxMenuItemJava™ API リファレンス MenuItemJava™ API リファレンス
ラジオメニュー項目 - JRadioButtonMenuItemJava™ API リファレンス MenuItemJava™ API リファレンス
自前描画領域 CanvasJava™ API リファレンス JComponentJava™ API リファレンス CanvasJava™ API リファレンス
コンテナ PanelJava™ API リファレンス JPanelJava™ API リファレンス CompositeJava™ API リファレンス
スクロール領域 ScrollPaneJava™ API リファレンス JScrollPaneJava™ API リファレンス CompositeJava™ API リファレンス
分割領域 - JSplitPaneJava™ API リファレンス SashJava™ API リファレンス
折りたたみ領域 - - ExpandBarJava™ API リファレンス
タブ - JTabbedPaneJava™ API リファレンス TabFolderJava™ API リファレンス
MDI - JDesktopPaneJava™ API リファレンス -
スプラッシュ SplashScreenJava™ API リファレンス - -
システムトレイ SystemTrayJava™ API リファレンス - TrayJava™ API リファレンス
カーソル CursorJava™ API リファレンス - CursorJava™ API リファレンス
ユーザシェル DesktopJava™ API リファレンス - ProgramJava™ API リファレンス
CVS 2008/03/29