オブジェクト指向プログラミングb 第11回


●GUI前編「もっとJavaを学ぶ 第8回 (最終回)(2004年4月 )」

 今月は,本連載の最終回として,GUIに関する後編をおとどけします。まず,メニュー
の使い方を紹介し,パネルを使用した高度なレイアウトの方法を説明します。そして,GUI
部品への操作を察知して何かしらの処理を行わせる,というイベント処理の方法について解
説します。

【メニューバーとプルダウンメニュー】
 代表的なコンポーネントは,すでに前回で紹介しましたが,ここではメニューバーと,
メニューバーから“引き出される”プルダウンメニューを紹介しておきましょう。

●メニューシステムの構造
 まず,メニューバーを持つメニューの構造を説明しておきましょう。Fig.1をみて下さい。
これは,一般的なメニューの構造を表しています。まず,メニューバーという,複数のメ
ニューを登録しておく場所があります(Fig.1-(1))。Fig.1の例では,「ファイル」と「編集」
というタイトルのメニュー2個が登録されています。

 メニューバーに登録されているメニュータイトルをクリックすると,メニューの全体が
現れます。Fig.1の例では,「ファイル」メニューの全体が表示されています(Fig.1-(b))。
 メニューの中には,選択肢がいくつかならんでいます。この選択肢を,メニュー項目
呼びます。Fig.1-(1)の「ファイル」メニューでは,「開く」,「最近開いた書類を開く」,
「閉じる」,セパレータ(区切り線,Fig.1-(e)),「印刷」の5項目が登録されています。
セパレータは,性質の異なるメニュー項目をグループに分けて見やすくするのに便利です。
Fig.1の例では,書類の開く・閉じると,印刷をセパレータを使って区分けしています。
 また,メニュー項目自体がメニューになっているという場合もあります。Fig.1の「ファ
イル」の例では,「最近開いた書類を開く」項目が,メニューになっており,そのメニュー
には「recent1」,「recent2」という2項目が登録されています("recent"という単語は,
"最近の"という意味)。このように,メニュー項目がメニューになっているメニューを,
サブメニューといいます(Fig.1-(d))。

●メニューを表すクラス
 Javaでは,メニューも簡単に使用することができます。AWT版では,メニューバーは,
MenuBar,メニューはMenu,メニュー項目はMenuItemというクラスで表現されています。
また,たいていのコンポーネントと同様に,SwingではAWT版のクラス名に頭文字として
Jをつけた名称のクラスが使われます。つまり,メニューバーは,JMenuBar,メニューは
JMenu,メニュー項目はJMenuItemというクラスで表されています。

●AWT版メニューを使う
 では,実際のプログラムに沿って,メニューの基本的な使い方を紹介しましょう。List 1
は,AWTでFig.1と同様のメニューを配置・表示するものです。まず,List 1-<①>でjava.awt
パッケージをインポートし,<2>でウィンドウを生成しています。
List 1 AWTMenu.java

 つぎに,「ファイル」メニューを作成しましょう。メニューを作成するには,Menu型オ
ブジェクトを
  new Menu( "メニュータイトル" )
と生成します。List 1-<3>では,"ファイル"というタイトルを持つメニューfileMenuを作成
しています。
 次に,メニュー項目を追加します。メニュー項目は,
  new MenuItem( "項目名" )
というようにしてMenuItem型オブジェクトとして生成することができます。List 1-<4>では,
"開く"という項目名のメニュー項目miOpenを生成しています。
 生成したメニュー項目は,メニューに登録することになります。Menuのメソッドadd()
使って,メニューにメニュー項目を登録できます。List 1-<5>では,
  fileMenu.add( miOpen );
として,ファイルメニューfileMenuにメニュー項目miOpenを追加しています。
 次にサブメニューを作成します。サブメニューは,本日的に普通のメニューとかわりません。
つまり,サブメニューもMenu型オブジェクトなのです。メニューにメニュー項目として他の
メニューを追加することで,サブメニューになるのです。
 List 1-<6>〜<10>は,サブメニュー「最近開いた書類を開く」を作成して「ファイル」
メニューにサブメニューとして登録しているところです。List 1-<6>では,「最近開いた書類
を開く」メニューrecentMenuを作成し,List 1-<7>〜<9>でメニュー項目「recent1」と
「recent2」を作成して,「最近開いた書類を開く」メニューに登録しています。
 作成した「最近開いた書類を開く」メニューをサブメニューとして「ファイル」メニューに
登録しているのがList 1-<10>の
  fileMenu.add( recentMenu );
という部分です。
 List 1-<11>では,「閉じる」メニューを作成し,「ファイル」メニューに追加しています。
 List 1-<12>は,MenuクラスのメソッドaddSeparator()を使って,「ファイル」メニュー
にセパレータを追加しています。List 1-<13>では,「印刷」メニューを作成して,「ファイル」
メニューに追加しています。Fig.1のメニューの例では,「ファイル」メニューの他に「編集」
メニューもあります。List 1-<14>では,その「編集」メニューを作成しています。
 メニューの仕上げに,メニューバーを作成し,作成した「ファイル」メニューと「編集」
メニューをメニューバーに追加しましょう。メニューバーの作成はList 1-<15>の
  MenuBar mbar = new MenuBar();
というように,単にMenuBar型オブジェクトを生成すればOKです。メニューバーにメニューを
追加するには,MenuBarクラスのadd()メソッドを使います。List 1-<16>の
  mbar.add( fileMenu );
  mbar.add( editMenu );
で,メニューバーにまず「ファイル」メニューを追加し,次に「編集」メニューを追加しています。
 さて,ウィンドウにメニューバーを追加するには,FrameクラスのsetMenuBar()メソッドを使
います。List 1-<17>では
  f.setMenuBar( mbar );
として,ウィンドウfにメニューバーmbarを追加しています。
 最後に,ウィンドウを可視化しているところがList 1-<18>です。List 1を実行すると,Fig.2の
ように,メニューが使用できるようになります。実際に動かして試してみてください。

 メニューバーに登録されるメニューは,左から右へ登録されていくことが分かります。この例では,
「ファイル」メニュー,「編集」メニューの順にメニューバーに追加したので(List 1-<16>),
メニューバーの左から右に「ファイル」メニュー,「編集」メニューが登録されています。また,
メニュー項目は,上から下へ登録されていきます。この例では,「開く」,「最近開いた書類を開く」,
セパレータ,「印刷」メニューの順に「ファイル」メニューに追加したので,「ファイル」メニュー
にはこの順で上から下にメニュー項目が登録されているわけです。なおMenuには,好きな位置に
メニュー項目を挿入するinsert()メソッドや,メニュー項目を削除するremove()メソッドなども用意
されています。
 ところでAWT版メニューでは,Fig.2-(1)とFig.2-(2)を比べるとわかるように,MacOS Xの場合,
メニューバーはウィンドウの外にあるのに対し,Windowsではウィンドウの中にメニューが配置さ
れます。

●Swing版メニューを使う
 次に,Swing版のメニューを使用してみましょう。List 1をSwing版に書き換えたのが,
List 2です。List 2-<①>〜<18>は,それぞれList 1-<①>〜<18>に対応していますので,
比較してみてください。
List 2 SwingMenu.java


 Swing版は,基本敵にAWT版と変わりません。Menu,MenuItem,MenuBarのかわり
にそれぞれJMenuJMenuItemJMenuBarを使用することが大きな違いです。また注意
すべきこととして,メニューバーをウィンドウに追加するには,JFrameクラスのsetJMenuBar()
を使わなければならない点があります。List 2-<17>では,
  f.setJMenuBar( mbar );
として,JFrame型ウィンドウにJMenuBar型メニューバーmbarを追加しています。
 List 2を実行すると,Fig.3のように表示されます。MacOS Xでは,ウィンドウの中に
メニューバーが配置されるのがAWT版との大きな違いです。また,メニューをよく観察す
ると,AWT版のメニューとSwing版メニューでは,外観が少し異なります。Fig.2-(2)と
Fig.3-(2)を比較すると,AWT版がWindows OSが提供する通常のメニューの外観を持って
いるのに対し,Swing版はWindows OSの通常のメニューとは異なる外観を持っています。



【パネルによる高度なレイアウト】
 さて,前回はレイアウトマネージャを使用した簡単なコンポーネントのレイアウト方法
について学習しました。しかし,それだけではちょっと複雑なレイアウトは実現できません。
 実は,パネルという特別なコンテナを使うと,より複雑なレイアウトを行うことができ
るのです。パネルは,他のパネルを含むことができます。当然,個々のパネルには異なった
レイアウトマネージャを設定できます。したがって,パネルを入れ子で配置することで,
階層的に複雑なコンポーネント配置が実現できる
のです。
 パネルを表すAWT版クラスは,Panelです。実際にパネルを使って,Fig.4-(1)のように
コンポーネントを配置してみましょう。手順は次のようになります。まず,2行2列のグリ
ッドレイアウトを持つパネル1にボタンを4個配置します(Fig.4-(1)(a))。そして,フロー
レイアウトを持つパネル2に,テキストエリアとボタンを配置します(Fig.4-(1)(b))。次に,
1行2列のグリッドレイアウトを持つパネル3に,パネル1とパネル2を追加します(Fig.4-(1)(c))。
そして,パネル3をウィンドウ(Fig.4-(1)(d))に追加します。

 実際のAW版プログラムが,List 3です。List 3-<①>でjava.awtパッケージをインポート,
List 3-<2>でFrame型ウィンドウを生成します。List 3-<3>でパネル1を生成し,List 3-<4>
で2行2列のグリッドレイアウトをパネル1に設定します。List 3-<5>で,ボタンを4個,パネル1
に追加します。
 List 3-<6>ではパネル2を生成し,List 3-<7>ではパネル2にフローレイアウトを設定します。
List 3-<8>で,パネル2にテキストエリアとボタンを追加します。
 List 3-<9>では,パネル3を生成し,List 3-<10>では,このパネル3に1行2列のグリッドレ
イアウトを設定しています。そして,ここが重要ですが,List 3-<11>で,パネル3にパネル1と
パネル2を追加しています。最後に,パネル3をウィンドウfに追加し(List 3-<12>),ウィンドウf
をパックして(List 3-<13>),ウィンドウfを可視化します(List 3-<14>)。
 List 3の実行結果がFig.4-(2)です。Fig.4-(1)の設計どうりにコンポーネントが配置されている
ことを確認してください。
List 3 AWTPanel.java

 同じことをSwing版で行うとList 3_2のようになります。Swingでは,PanelではなくJPanelを
使用することがAWT版との主な違いです。もちろん,他のコンポーネントもSwing版のものを使
用します。List 3_2-<①>〜<14>は,それぞれList 3-<①>〜<14>に対応していますので,確認
してみてください。
List 3_2 SwingPanel.java

【コンポーネントの実装・継承関係】
 ここで,JavaにおけるGUIコンポーネントを表すクラスや各種インタフェイスの主な実装・
継承関係をFig. 5に紹介しておきます。スーパークラスが書かれていないクラスのスーパー
クラスはObjectです。各クラスやインタフェイスの左下にある三角がグレイのものがAWT
所属のもの,黒いものSwing所属のものです。あまり重要でないいくつかのクラス・インタ
フェイスおよび実装・継承関係は省いていますが,APIの参照時などに役に立つでしょう。
(PDF版はこちら。印刷して手元に置いておくと,自分でプログラムを書くときに参考になるでしょう)


 Fig.5では,クラス・インタフェイスをいくつかのグループにわけています。このグループ
分けは便宜的なものですが,把握しているとFig.5をより活用できるでしょう。
 まず,基幹コンポーネント部分が,Fig.5の上部にある<①>の部分です。すべてのコンポー
ネントのスーパークラスComponentがあり,そのサブクラスとして,すべてのコンテナの
スーパークラスであるContainerクラスがあります。そのサブクラスが,Swing系コンポー
ネントのスーパークラスJComponentです。
 Fig.5-<①>のすぐ右下にある<2>が,Containerのサブクラス群としてのウィンドウ系コン
ポーネント群
です。まず,ContainerのサブクラスとしてWindowがあり,そのサブクラスと
して,Frame,Dialog,JWindowがあります。Swing系ウィンドウのJFrameとJDialogはそれ
ぞれ,FrameとDialogのサブクラスとなっています。JComponentクラスのサブクラスJInternalFrame
は,Windowのサブクラスではありませんが,ウィンドウの内部に作成される擬似的なウィン
ドウです。
 ウィンドウ系コンポーネントの左にあるFig.5-<3>が,アップレット・AWT系パネルコン
ポーネント群
です。AWT版のパネルであるPanelや,ScrollPane,ScrollaPaneAdjustableと
いうAWT版ペインのクラス,そして,古いバージョンのアップレットjava.applet.Applet
そのサブクラスとしてのSwing版アップレットのJAppletがあります。
 アップレットは,ウェブブラウザ上で実行できるアプリケーションの一形態で,読者の皆さ
んならご存じでしょう。ごらんのように,Applet,JAppletは,AWT版パネルであるPanelサ
ブクラスとして定義されているのです。
 そして,Fig.5-<2>のJWindow,JFrame,JDialog,JInternalFrame,Fig.5-<3>のJApplet
の5つのクラスは,Fig.5-<2>のRootPaneContainerインタフェイスを実装しています。
RootPaneContainerは,前回お話ししたSwing系のウィンドウが持っているコンテントペイン
を返すメソッドgetContentPane()を定義しています。つまり,RootPaneContainerを実装し
ているこれら5つのクラスこそが,コンテントペインを持っているクラスであることになります。
 アップレット・AWT系パネルコンポーネント群の下のFig.5-<4>が,AWT系メニューコン
ポーネント群
です。さらにその下のFig.5-<5>が,AWT版の一般コンポーネント群です。だい
たい,以上でAWT系コンポーネントはすべてカバーできています。残りは全部Swing系コンポ
ーネントです。
 Fig.5の右側にあるのが,Swing系メニューコンポーネント群です。Fig.5の中央には,Swing
系ボタンコンポーネント群
(fig.5-<7>),とSwing系ペインコンポーネント群(Fig.5-<8>)があり
ます。Fig.5の中央やや下には,Swing系の一般コンポーネント群(Fig.5-<9>)があります。その
下には,Swing系コンボボックスコンポーネント群(Fig.5-<10>)があります。Fig.5の下端には,
Swing系テキストコンポーネント群(fig.5-<11>),Swing系テーブルコンポーネント群(fig.5-<12>)
があります。
 なお,Fig.5の左下にあるFig.5-<13>は,次節で説明するイベント関係のインタフェイス群
です。

【イベント処理】
 さて,ここまでサンプルプログラム上で様々なコンポーネントを生成し,ウィンドウに
配置してきました。しかし,ボタンを押したり,メニューを選んでも何もおこりませんで
した。なぜなら,ボタンが押されたことや,メニューが選ばれたことを察知して,何かし
らの動作をさせるようなコードを書いてなかったからです。

●イベント
 では,ソフトウェア使用者の
  ・ボタンへのクリック
  ・メニューの選択
  ・キーボードからの文字入力
などの操作が行われたことを察知し,対応した動作をさせるには,どうすればいいのでし
ょう。その鍵となるのが,イベントという考え方です。イベントは英語でevent,日本語
で言えば“出来事”という意味です。日本でも,コンサートや運動会やお祭りなど,行事
のことをイベントといいますね。
 GUIを持つソフトウェアのプログラミングでは,ボタンやメニューなどのGUI部品への操
作が行われたことを,“イベント”としてプログラムに通知するのが一般的
です。具体的
には,何かしらGUI部品に対して操作が行われると,オペレーティングシステムやGUIライ
ブラリが,その内容を表すイベントオブジェクトを生成します。
 何かしらの操作が行われ,対応するイベントオブジェクトが生成されることを,“イベ
ントが発生した”
とか,“イベントが発行された”“イベントが通知された”などと言
います。
 Javaでは,イベントを表すスーパークラスとしてjava.util.EventObjectが用意されてお
り,様々な操作に対応するイベントクラスがこのEventObjectのサブクラスとして用意さ
れています。
 たとえば,Javaではボタンをクリックしたり(Fig.6-<①>),メニュー項目を選択した場合,
java.awt.event.ActionEvent型イベントオブジェクトが生成されます(Fig.6-<2>)。この
ActionEvent型イベントは,コンポーネントになにかしらの“標準的な操作”が起こった
ことを表します
。この“標準的な操作”が,ボタンの場合は「ボタンを押した」という操作
であり,メニュー項目の場合は「メニュー項目を選んだ」という操作というわけです。
 Table 1の左側に,Javaで使われる主なイベントクラスをあげておきます。所属欄がAWT
のイベントクラスは,java.awt.eventパッケージで定義されており,所属欄がSwingのイベ
ントクラスは,javax.swing.eventパッケージで定義されています。GUIコンポーネントに
よって,発生するイベントが異なることに注意してください。たとえば,ActionEvent型
イベントは多くのコンポーネントで発生しますが,TreeModelEvent型イベントは,JTree型
コンポーネントでしか発生しません。
 なお,操作が行われたGUI部品,つまりイベントの発生原因となったGUIコンポーネント
を,イベントソースと呼びます。

(Table 1のPDF版はこちら)

●イベントリスナ
 こうして生成されたイベントを待ち受けるのが,各GUIコンポーネントに登録されてい
イベントリスナ(event listener)と呼ばれるオブジェクトです。ご存じのように英単語
のlistenは,“意識して聴く”,“聞き耳を立てる”というような意味で,イベントリスナ
とは,“イベントがくるのを待ち受ける”というような意味です。
 イベントリスナは,対応するイベントごとに用意されているインタフェイス(リスナイン
タフェイス
)をプログラマが実装して作成します。Table 1の右側に,主なイベントに対応
するイベントリスナインタフェイスをあげておきます。イベントと同様に,所属欄がAWT
のイベントリスナインタフェイスは,java.awt.eventパッケージで定義されており,所属
欄がSwingのイベントリスナインタフェイスは,javax.swing.eventパッケージで定義され
ています。
 たとえば,Table 1を見れば,ActionEventに対応するイベントリスナインタフェイス
として,インタフェイスActionListenerがあることがわかりますね。このように,XXXXEvent型
イベントには,対応するリスナインタフェイスXXXXListenerが用意されている
のです。
Fig.6の例では,ActionListener型イベントリスナがボタンオブジェクトb1に登録されてい
ます(Fig.6-<0>)。
 リスナインタフェイスの実装の仕方を,ActionListenerの例で説明しておきましょう。
ActionListenerインタフェイスの定義は,だいたい次のようになっています。
  public interface ActionListener extends EventListener {
   public void actionPerformed(ActionEvent e);
  }
このactionPerformed()のように,リスナインタフェイスにはいくつか抽象メソッドが用意
されています。これらの抽象メソッドの引数は,対応するイベントオブジェクトになってい
ます。上のactionPerformed()メソッドの例では,引数はActionEvent型オブジェクトです。
 JavaのGUIシステムは,イベントオブジェクトが生成されると,イベントソースに登録さ
れているイベントリスナからイベントの種類に対応するイベントリスナの適切なメソッドを
呼び出します
。Fig.6の例では,b1に登録されているActionListener型オブジェクトの
actionPerformed()を呼び出すことになります。
 ですから,ActionListenerを実装するクラスを定義し,そこでactionPaformed()メソッド
をオーバライドして,ボタンが押されたときの処理を記述しておけば,ボタンが押されたと
きに望みの処理が実行されることになります。

●リスナアダプタ
 ところで,イベントリスナインタフェイスには,抽象メソッドをたくさん持っているも
のもあります。たとえば,ComponentListenerは,
  void componentHidden ( ComponentEvent e );
  void componentMoved ( ComponentEvent e );
  void componentResized ( ComponentEvent e );
  void componentShown ( ComponentEvent e );
という4個の抽象メソッドを持っていて,それぞれGUIコンポーネントが不可視になる,位置
が変わる,サイズが変わる,可視になるときに呼び出されます。もし,コンポーネントのサ
イズが変わったときだけに特定の処理を行いたい場合でも,これら4つのメソッドをオーバ
ライドしてやらなくてなりません。なぜなら,抽象メソッドをすべてオーバライドしないと,
そのサブクラスは抽象クラスになって,オブジェクトを作成することができなくなるからです。
これではめんどうですね。
 そこで,多くの抽象メソッドを持つリスナインタフェイスには,アダプタ(adapter)と呼ば
れる実装クラスが用意されています。リスナインタフェイスXXXXListenerに対応するアダプ
タクラスの名称は,XXXXAdapterです

 このアダプタクラスでは,スーパーインタフェイスのメソッドすべてを空の定義でオーバ
ライドしています。たとえば,ComponentListenerのアダプタクラスcomponentAdapterの
定義は,次のようになっています。
  public abstract class ComponentAdapter implements ComponentListener{
   public void componentResized ( ComponentEvent e ) {}
   public void componentMoved ( ComponentEvent e ) {}
   public void componentShown ( ComponentEvent e ) {}
   public void componentHidden ( ComponentEvent e ) {}
  }
全メソッドが空の定義{}でオーバライドされているのがわかりますね。ですから,コンポーネ
ントのサイズが変更された時だけに処理を行うイベントリスナクラスを作りたければ,
ComponentListenerを直接実装するのではなく,ComponentAdapterのサブクラスを
  class クラス名 extends ComponentAdapter {
   public void componentResized ( ComponentEvent e ) {
    /* 処理をここに記述 */
   }
  }
というように,必要なメソッドだけをオーバライドすればいいことになります。
 各リスナに用意されているアダプタもTable 1の右側にあげておきましたので参考に
してください。

●イベント処理記述の手順
 ここで,XXXXEvent型イベントを処理するための記述手順をまとめておきましょう。まず,
XXXXEventに対応するイベントリスナインタフェイスXXXXListenerを(直接または間接的に)
実装するリスナクラスを作成し,抽象メソッドをオーバライドしてイベント処理時に実行した
い処理を記述します。そして,イベントソースとなるGUIコンポーネントに,そのリスナクラス
のオブジェクト(イベントリスナ)を登録します。GUIコンポーネントに,イベントリスナを
登録するには,GUIコンポーネントのaddXXXXListener()メソッドを使用することができます

●イベント処理の流れ
 次に,プログラム実行時のイベント処理の流れをまとめると,次のようになります。まず,
初期化の時点であらかじめイベントリスナを対象となるGUIコンポーネントオブジェクト(イベ
ントソース)に登録します(Fig.6-<0>)。イベントソースのGUI部品に何かしらの操作が行われ
る(Fig.6-<①>)と,対応するイベントオブジェクトが生成されます(Fig.6-<2>)。
 そして,イベントソースに登録されているイベントリスナのうち,発生したイベントに対応
するものが選ばれ,状況に応じて適切なメソッドが選ばれて呼ばれます(Fig.6-<4>)。

●プログラム終了時の処理
 ところで,いままでのGUIサンプルプログラムでは,終了時の処理を省略していたので,
コンソールからControl+Cというキーコンビネーションで強制終了させていました。しかし,
イベント処理でプログラムを正常に終了させることができます。
 メインウィンドウが閉じられるときに,プログラムを正常終了させることにしましょう。
ウィンドウに関するイベントクラスはWindowEventで,そのリスナはWindowListenerです。
ウィンドウが閉じるときには,WindowListenerのwindowClosing()メソッドが呼ばれます
ので,このメソッドを,プログラムを終了させるメソッドSystem.exit()を呼ぶようにオー
バライドすればいいわけです。なお,System.exit()メソッドは,int型の引数を1個持ち,
その値が0の場合はプログラムが正常に終了したことを表し,0意外の場合は異常終了した
ことを表します。
 WindowListenerには7個もメソッドがあるので,アダプタクラスWindowAdapterのサブ
クラスとして,イベントリスナクラスを定義すればいいでしょう。もちろん,作成したイベ
ントリスナオブジェクトを,メインウィンドウオブジェクトに登録しておかなくてはなりま
せん。

●イベント処理の実例(プリントではAWT版のList 4は紹介されていません。プリントではSwing版のList 4_2がList 4として紹介されています。)
 ここで,イベント処理の簡単な実例を見てみましょう。List 4は,AWTコンポーネント
を使ったイベント処理の例です。このアプリケーションは,ボタンを持ったメインウィン
ドウを最初に1個表示し,そのボタンを押すと,1個ずつサブウィンドウが増えていくとい
うものです。
List 4 AWTTestEvent1.java (プリントでは紹介されていない。プリントのList 4は次のSwingTestEvent1.javaである)


 では,List 4を見ていきましょう。List 4-<①>では,AWTパッケージをインポートしてい
ます。そして,List 4-<2>では,イベントを扱うためにjava.awt.eventパッケージをインポ
ートしています。
 List 4-<3>は,サブウィンドウの初期位置x0,y0と,サブウィンドウの数nです。List 4-<4>
ではメインウィンドウfを生成し,List 4-<5>では,ボタンb1を生成してメインウィンドウf
に追加しています。
 さて,List 4-<6>からがイベント処理の具体的な例になります。まず,ボタンに登録する
アクションリスナを作成します。List 4-<6>〜<8>がその部分になります。List 4-<6>に
あるように,ActionListenerを実装するクラスとして,Button1Actionクラスを定義してい
ます。このように,クラスの内部や,メソッドの内部で定義されるクラスを,内部クラス
呼びます。
 この内部クラスButton1Actionの定義の中で,actionPerformed()メソッドをオーバライ
ドしているのが,List 4-<7>の部分です。ここでは,サブウィンドウを生成し,座標をずら
して表示しています。そして,このリスナクラスButton1Actionのオブジェクトをボタンb1
へ登録しているのが,List 4-<9>です。
 次に,アプリケーションの正常終了を行わせるイベント処理を記述します。List 4-<10>
〜<12>で,WindowAdapterのサブクラスとしてイベントリスナクラスMainWindowListener
を定義しています。ここでは,List 4-<11>にあるようにSystem.exit()メソッドを呼び出す
ように,windowClosing()メソッドをオーバライドしています。こうして作成したイベント
リスナMainWindowListener型のオブジェクトをメインウィンドウfに登録指定しているのが,
List 4-<13>です。最後のList 4-<14>でメインウィンドウfを可視化しています。
 List 4を実行すると,最初にボタンのついたメインウィンドウが表示されます。そして,
ボタンを押すと,次々にサブウィンドウが表示されます。メインウィンドウを閉じると,正常
にアプリケーションが終了します。実際に実行して,イベント処理がうまくはタラしている
ことを確認してください。

●匿名クラスとしてのリスナクラス
 実は,List 4はイベント処理そのものとしては正常に動作しますが,リスナクラスの定義
の書き方にいささか古い記述があります。リスナクラスは,通常,匿名クラス(無名クラス
も言う)という,名前のないクラスを使うのが主流です。

 匿名クラスは,Fig.7のように定義します。特徴的なのは,匿名クラスは定義と生成が同じ
場所で行われる
ことです。通常のクラスは,名前がありますから,
  new クラス名()
とすれば,定義とは別のところでクラス名を指定することでオブジェクトの生成ができました。
しかし,匿名クラスは名前がないので,定義と同じ場所でオブジェクトを生成するしかありま
せん。
 一見,この点は不便に感じますが,逆に利点もあるのです。一般的に,クラス定義と使用
場所は近いにこしたことはありません。この点で,匿名クラスは非常に優れているのです。
リスナクラスは,コンポーネントオブジェクト1つ1つに定義してやる必要があり,しかも他
の目的で再利用することは滅多にありません。ですから,匿名クラスとしてリスナクラスを
定義し,同時にオブジェクトを生成してGUIコンポーネントオブジェクトに登録してしまうの
がスマートな方法なのです。
 実際に,List 4をSwing版になおした上で,リスナを匿名クラスで定義するようにした例を
List 4_2に示します。
List 4_2 SwingTestEvent1.java (プリントで紹介されているList 4は,このList 4_2です。)


List 4_2-<①>でSwingパッケージを,List 4_2-<2>でjava.awt.eventパッケージをインポート
しています。List 4_2-<3>は,サブウィンドウの初期位置と数です。List 4_2-<4>,<5>は,
メインウィンドウの生成とボタンの生成です。
 List 4_2-<6>からが,イベント処理部分です。List 4_2-<6>〜<8>で,addActionListener()
メソッドを呼び出して,匿名クラスオブジェクトをリスナとしてボタンb1に登録しています。
List 4_2-<7>が,ActionListenerを実装する匿名クラスを定義し,オブジェクトを生成して
いる部分です。
 同様に,List 4_2-<9>〜<12>では,addWindowListener()メソッドを呼び出して,アプ
リケーション終了に関するイベントリスナをメインウィンドウfに登録しています。List 4_2-<10>
が匿名のWindowAdapterのサブクラスを定義し,そのオブジェクトを生成している部分です。
このList 4_2-<9>〜<11>の部分は,多くのGUIアプリケーションで共通する部分ですから,
常套句としておぼえておきましょう。
 ここまで,駆け足でJavaにおけるGUIの利用の仕方を紹介してきました。各コンポーネント
の深い使いこなしまでは触れられませんでしたが,JavaにおけるGUIの全体像と,基本的な使
い方はお話しできたと思います。あとは,参考書やJava APIを参照するなどして,いろいろと
チャレンジしてみてください。


戻る