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>