イベントのまとめ

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>

アダプターを使う

リスナー・インタフェースはインタフェースなので、宣言されたメソッドは、不必要なものも含めて必ず実装する必要があります。インタフェースであるおかげで、複数のリスナーを利用可能であり、継承を別途実装することができるのですが、不要なコードが増えることも事実です。他のクラスの継承の必要が無ければアダプター・クラスを継承して使うのが便利です。

アダプター・クラスとインタフェースの関係は次のようになっています。

インタフェースとアダプターの関係
リスナー・インタフェースアダプター・クラスメソッド
ActionListenernone actionPerformed
AdjustmentListenernone adjustmentValueChanged
ComponentListenerComponentAdapter componentHidden
componentMoved
componentResized
componentShown
ContainerListenerContainerAdapter componentAdded
componentRemoved
FocusListenerFocusAdapter focusGained
focusLost
ItemListenernone itemStateChanged
KeyListenerKeyAdapter keyPressed
keyReleased
keyTyped
MouseListenerMouseAdapter mosueClicked
mouseEntered
mouseExited
mousePressed
mouseReleased
MouseMotionListenerMouseMotionAdapter mouseDragged
mouseMoved
TextListenernone textValueChanged
WindowListenerWindowAdapter windowActivated
windowClosed
windowClosing
windowDeactivated
windowDeiconified
windowOpened

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>


Copyright © 2001 SUGAI, Manabu. All Rights Reserved.