last modified: Sep./9th/2002
最初に、リスナークラスを別途作成するイベント処理を実装してみます。ここでは、ボタンのクリックというイベントを取得して入力した整数の80%の値を出力します。
イベント・リスナーは、取得するイベントに対して対応するインタフェースを実装することで作ります。ボタンのクリックはjava.wat.event.ActionListenerを実装します。このインタフェースにはactionPerform()メソッドだけが宣言されており、ボタンクリック時には自動的にはアクション・リスナー・オブジェクトのこのメソッドが呼ばれます。
EventSample.java
:
import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class EventSample extends Applet { // 部品の変数をメンバーとして宣言 Label teiLabel, wariLabel; TextField teiField, wariField; Button calcButton; public void init() { // 部品作成 teiLabel = new Label("定価"); wariLabel = new Label("割引値"); teiField = new TextField("0", 10); wariField = new TextField("0", 10); calcButton = new Button("計算"); // アクションリスナー作成 EventHandler eh = new EventHandler(this); // アクションリスナー登録 calcButton.addActionListener(eh); add(teiLabel); add(teiField); add(calcButton); add(wariLabel); add(wariField); } } class EventHandler implements ActionListener { EventSample appl; // コンストラクタでイベントソースの取得 EventHandler(EventSample obj) { appl = obj; } // イベント処理の実装 public void actionPerformed(ActionEvent e) { // イベントソースのteiFieldのテキストを取得 String strTei = appl.teiField.getText(); // Stringをint型に変換 int iTei = Integer.parseInt(strTei); int iWari = iTei * 80 /100; // イベントソースのwariFieldにテキストをセット appl.wariField.setText(Integer.toString(iWari)); } }
EventDemo.html
:
<p><applet code="EventSample" width="200" height="100"> アプレットが実行できない場合の代替内容。 </applet></p>
AWTイベントは低レベルとセマンティックの二つに分けられます。低レベル・イベントはコンポーネントのリサイズやフォーカス、マウス・カーソルの押下などのウィドウ・システムに直結するイベントです。セマンティック・イベントはコンポーネントのUIとしての意味に依存する高レベルのイベントです。具体的には、ボタンの押下、リストのダブルクリック、メニューの選択などです。
ここでイベント・リスナー・インタフェースを列挙します。
低レベル・イベント・インタフェース java.util.EventListener java.awt.event.ComponentListener java.awt.event.ContainerListener java.awt.event.FocusListener java.awt.event.KeyListener java.awt.event.MouseListener java.awt.event.MouseMotionListenr java.awt.event.WindowListener セマンティック・リスナー・インタフェース java.util.EventListener java.awt.event.ActionListener java.awt.event.AdjustmentListener java.awt.event.ItemListener java.awt.event.TextLisntener
以上のリスナー・インタフェースの詳細はAPI仕様書を参照ください。
先にあげた例とは別のイベント処理の実装方法を紹介します。
イベント・リスナーの条件はイベント・リスナー・インタフェースを実装することです。従って、Appletクラスを継承したクラス自身をイベント・リスナーにすることも可能です。
次のサンプルは低レベル・イベントであるマウス・カーソルの動作を拾っています。対応するインタフェースはMouseLisntenerであり、このインタフェースで宣言されているメソッドは次の五つです。
このインタフェースを実装するクラスでは、これら五つのメソッドを全て実装する必要があります。
MouseEventHandlerApplet.java
:
import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class MouseEventHandlerApplet extends Applet implements MouseListener { Label lbl1, lbl2; int ctr = 0; public void init() { lbl1 = new Label("Mouse Cursor is wanted.", Label.CENTER); lbl2 = new Label(" Initial State ", Label.CENTER); lbl1.addMouseListener(this); add(lbl1); add(lbl2); } // MouseLisnterの実装 public void mouseEntered(MouseEvent e) { lbl2.setText("Cursor is Entered."); lbl1.setForeground(Color.blue); lbl2.setBackground(Color.blue); lbl2.setForeground(Color.white); } public void mouseExited(MouseEvent e) { lbl2.setText("Cursor is Exited."); lbl1.setForeground(Color.red); lbl2.setBackground(Color.red); lbl2.setForeground(Color.white); } public void mouseClicked(MouseEvent e) {} public void mousePressed(MouseEvent e) {} public void mouseReleased(MouseEvent e) {} }
ここでは、Labelのインスタンス化時にテキスト文字列の配置を指定しています。指定できる値はLabel.LEFT, Label.RIGHT, Label,CENTERの三つです。また、イベント・リスナーではマウス・カーソルの位置に応じて二つの処理を実装しています。何れの場合もLabelの前景色と背景色を指定しています。利用しているメソッドestForeground(), setBackground()はComponentクラスから継承したものです。
MouseEventHandlerDemo.html
:
<p><applet code="MouseEventHandlerApplet" width="200" height="100"> アプレットが実行できない場合の代替内容。 </applet></p>
ここまで作成してきたクラスは、パッケージのメンバーである最上位のクラスでした。クラスの内部に別のクラスを記述することができます。クラス内部に記述されたクラスをネスト型(nested class)と呼びます。ネスト型クラスは、クラスのメンバーになるもの、メソッド内部に記述するものなどが許されます。特に、static宣言されていないネスト型クラスを内部クラス(inner class)と呼びます。また、内部クラスをメンバーに持つクラスは包含クラス(enclosing class)と呼びます。アクション・リスナー・クラスは内部クラスとして実装することが多くあります。
クラスのメンバーとしての内部クラスにprivate修飾子を宣言すれば他のクラスから隠蔽できます。即ち、包含クラス内部でしか利用できなくなるので、他のクラスから不用意に呼ばれることがなくなります。
import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class InnerClassDemo extends Applet { TextArea input, output; Button btn; innerEventHandler ieh; public void init() { ieh = new innerEventHandler(); btn = new Button("Copy"); input = new TextArea(5, 20); output = new TextArea(5, 20); btn.addActionListener(ieh); add(input); add(btn); add(output); } private class innerEventHandler implements ActionListener { public void actionPerformed(ActionEvent e) { String str = input.getText(); output.setText(str); } } }
InnerClassDemo.html
:
<p><applet code="InnerClassDemo" width="200" height="250"> アプレットが実行できない場合の代替内容。 </applet></p>
代理モデルのメリットは、イベント処理のロジックを拡張しやすく、再利用しやすくすることにあります。しかし、拡張性/再利用性を考えない場合はコードが多いことが気になります。そこで、使い捨てのリスナーは次のような匿名クラス (anonymouse class) にします。
AnonymousClassDemo.java
:
iimport java.awt.*; import java.awt.event.*; import java.applet.Applet; public class AnonymousClassDemo extends Applet { Button btn; Label label; public void init() { btn = new Button("おみくじ"); label = new Label("運勢"); btn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { int i = (int) (10.0 * Math.random()); switch (i) { case 0: label.setText("大吉"); break; case 1: label.setText("中吉"); break; case 2: label.setText("小吉"); break; case 3: label.setText("吉"); break; case 4: label.setText("半吉"); break; case 5: label.setText("末吉"); break; case 6: label.setText("末小吉"); break; case 7: label.setText("凶"); break; case 8: label.setText("半凶"); break; case 9: label.setText("大凶"); break; } } }); add(btn); add(label); } }
Math.random()は0以上1未満の乱数を発生させるメソッドです。ここでは10倍して0以上10未満の整数を取得し、switch構文で対応する処理を割り振っています。
AnonymousClassDemo.html
:
<p><applet code="AnonymousClassDemo" width="100" height="50"> アプレットが実行できない場合の代替内容。 </applet></p>
リスナー・インタフェースはインタフェースなので、宣言されたメソッドは、不必要なものも含めて必ず実装する必要があります。インタフェースであるおかげで、複数のリスナーを利用可能であり、継承を別途実装することができるのですが、不要なコードが増えることも事実です。他のクラスの継承の必要が無ければアダプター・クラスを継承して使うのが便利です。
アダプター・クラスとインタフェースの関係は次のようになっています。
リスナー・インタフェース | アダプター・クラス | メソッド |
---|---|---|
ActionListener | none | actionPerformed |
AdjustmentListener | none | adjustmentValueChanged |
ComponentListener | ComponentAdapter |
componentHidden |
ContainerListener | ContainerAdapter |
componentAdded |
FocusListener | FocusAdapter |
focusGained |
ItemListener | none | itemStateChanged |
KeyListener | KeyAdapter |
keyPressed |
MouseListener | MouseAdapter |
mosueClicked |
MouseMotionListener | MouseMotionAdapter |
mouseDragged |
TextListener | none | textValueChanged |
WindowListener | WindowAdapter |
windowActivated |
AdapterDemo.java
:
import java.awt.*; import java.awt.event.*; import java.applet.Applet; public class AdapterDemo extends Applet { Label lbl1, lbl2, lbl3; TextField txt; public void init() { EventHandlingAdapter eha = new EventHandlingAdapter(this); lbl1 = new Label("ゼウス"); lbl2 = new Label("ハデス"); lbl3 = new Label("ポセイドン"); txt = new TextField(30); lbl1.addMouseListener(eha); lbl2.addMouseListener(eha); lbl3.addMouseListener(eha); add(lbl1); add(lbl2); add(lbl3); add(txt); } } class EventHandlingAdapter extends MouseAdapter { AdapterDemo ad; EventHandlingAdapter(AdapterDemo obj) { ad = obj; } public void mouseEntered(MouseEvent e) { if (e.getSource() == ad.lbl1) { ad.txt.setText("Zeus: 天空の神。羅神)Jupiter"); } else if (e.getSource() == ad.lbl2) { ad.txt.setText("Hades: 冥界/地底の神。羅神)Pluto"); } else if (e.getSource() == ad.lbl3) { ad.txt.setText("Poseidon: 海の神。羅神)Neptune"); } } }
イベント・オブジェクトのgetSource()
メソッドは、イベントが発生したコンポーネントの識別IDを取得しています。例えばe.getSource() == ad.lbl1
は、e
が発生したコンポーネントが、ad
オブジェクトのlbl1
に等しいかどうかを評価しています。
AdapterDemo.html
<p><applet code="AdapterDemo" width="300" height="60"> アプレットが実行できない場合の代替内容。 </applet></p>