目次
プログラムの実行中にボタンをクリックされたとか、マウスがクリックされた、キーボードのキーが叩かれた、などのユーザーの行動は Java のシステムにイベントと呼ばれる割込み処理要求シグナルが発生する。これを感知し、適切な処理プログラムを組み込んでおけば、動的な情報処理が可能になる。
イベントには、ActionEvent(ボタンがクリックされた、など)、MouseEvent(マウスがクリックされた、ドラッグされた、など)、WindowEvent(ウィンドウの「閉じる」ボタンがくりっくされた、など)、KeyEvent(キーボードからタイプされた、など)、TextEvent(テキストが変更された、など)などがある。
イベント処理のプログラム(メソッド)はリスナーと呼ばれるインターフェースで仕様が決められているので、そのリスナーを実装し、その中のメソッドの定義を与えれば、処理可能になる。
例えば、マウスクリックイベントの場合、イベントを処理するのは mouseClicked() メソッドで、それを含む MouseListener インターフェースを実装し(implements MouseListener)、実際にイベントが発生するところにそれを知らせ(addMouseListener)、処理メソッド mouseClicked() を定義する。
public class MyFrame3 extends Frame implements MouseListener {
MyFrame3() {
...
this.addMouseListener(this);
...
}
...
public void mouseClicked(MouseEvent me) {
...
}
}
プログラムの例を載せる。
public class MyFrame3 extends Frame implements MouseListener {
MyFrame3() {
this.setSize(200, 200);
this.addMouseListener(this);
this.setVisible(true);
}
public static void main(String[] args) {
new MyFrame3();
}
public void mouseClicked(MouseEvent me) {
System.out.println("マウスがクリックされた" + me.getX() + "," + me.getY());
}
public void mouseEntered(MouseEvent me) {
System.out.println("マウスがウィンドウに入った");
}
public void mouseExited(MouseEvent me) {
System.out.println("マウスがウィンドウから出た");
}
public void mousePressed(MouseEvent me) {
System.out.println("マウスがプレスされた");
}
public void mouseReleased(MouseEvent me) {
System.out.println("マウスがリリースされた");
}
}
実行結果の例:
マウスがウィンドウに入った
マウスがプレスされた
マウスがリリースされた
マウスがクリックされた:45,80
マウスがウィンドウから出た
mouseClicked() 以下の5つのメソッドがイベント処理用のプログラムで、それを組み込むために、フレームのコンストラクタで addMouseListener(this) メソッドを実行している。それを可能にするために、クラスには MouseListener インターフェースが実装されている。MouseEvent クラスの getX(), getY() メソッドを使って、クリックされた座標を知ることができる。
実行して、マウスをウィンドウの中に動かし、クリックし、ウィンドウの外に出る、という動作に対する結果が実行結果の例に書かれている。クリックするとボタンをプレスした、リリースした、クリックした、という3つのイベントがその順番に発生するので、それらの処理プログラムが起動した結果このような表示が得られることになる。
イベントはリスナーで検知されるので、「implements リスナー」という記述が必要になるが、そうなると、インターフェースの一般規則として、そこで定義されているすべてのメソッドを実装しなければいけない。上の例の MouseListener インターフェースには5つのメソッドがあり、実際に必要とするのは mouseClicked() だけという場合に、他の4つのメソッドにも実体を与えなけれいけないという制約はかなり煩わしい。
そうしなくても良いように、リスナーを implement したクラスが用意されている。それをアダプターという。アダプターはクラスなので、必要なメソッドだけ上書きすれば良いことになる。その代償として、2つのクラスを親クラスとすることはできないという制約条件により、アダプターを継承したクラスを新たに定義しなければならない。
処理が大掛かりでなければ、イベントが発生する実行プログラム(クラス)の中で独自にクラスを定義するという方法が有効である。このように定義されたクラスは内部クラスと呼ばれる。通常のクラスの定義と同じにすればよい。内部クラスを含むクラスで有効な変数やメソッドは内部クラスの中でも使うことができる。内部クラスでイベント処理を実行することを示すために、addMouseListener() の引数に、その内部クラスのインスタンスを指定する。
例えば、次は上のプログラム例をアダプターを使って書き換えたものである。MouseAdapter を継承した MyMouseAdapeter クラスを定義し、その中で mouseClicked() メソッドだけをオーバーライドする。コンストラクタで、addMouseListener(new MyMouseAdapter()) を実行することにより、マウスイベント処理を MyMouseAdapter クラスで実行することを指定している。
public class MyFrame3 extends Frame {
MyFrame3() {
this.setSize(200, 200);
this.addMouseListener(new MyMouseAdapter());
this.setVisible(true);
}
public static void main(String[] args) {
new MyFrame3();
}
class MyMouseAdapter extends MouseAdapter {
public void mouseClicked(MouseEvent me) {
System.out.println("マウスがクリックされた:" + me.getX() + "," + me.getY());
}
}
}
インターフェースを実装することはないので、implements が消えている。実行すると、確かに、クリックされたという情報だけが表示される。
MyMouseAdapter というクラスの名前は addMouseListener() の引数として使われているだけなので、その名前を引数とする代わりに、定義そのものを引数とするというやり方がある。上のプログラムを書き換えると次のようになる。
this.addMouseListener(new MouseAdapter() {
public void mouseClicked(MouseEvent me) {
System.out.println("マウスがクリックされた:" + me.getX() + "," + me.getY());
}
});
あまり見やすくないが、その場限りでした使わないクラスであれば、この定義の仕方も悪くはない。これを無名内部クラスという。ウィンドウの「閉じる」ボタン対処のプログラム WindowAdapter の中の windowClosing() メソッド)では、単に System.exit() メソッドを実行するだけなので、この無名内部クラス利用がスマートである。
this.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent we) { System.exit(0); }
});
イベントには以下のような種類がある。リスナーは xxxListener、アダプターは xxxAdapter、イベントはxxxEvent と命名されている。
| 発生イベント | 種類 | リスナー | アダプター | メソッド |
|---|---|---|---|---|
ActionEvent |
ボタンほか | ActionListener |
|
actionPerformed() |
MouseEvent |
マウス | MouseListener |
MouseAdapter |
mouseCLicked() など(*!) |
MouseEvent |
マウスの動き | MouseMotionListener |
MouseMotionAdapter |
mouseDragged(), mouseMoved() |
WindowEvent |
ウィンドウ関連 | WindowListener |
WindowAdapter |
windowClosed() など(*2) |
KeyEvent |
キーボード | KeyListener |
KeyAdapter |
keyTyped(), keyPressed(), keyReleased() |
TextEvent |
テキスト変更 | TextListener |
TextAdapter |
textValueChanged() |
MouseListener のメソッドはその他に mouseEnterd(), ...Exited(), ...Pressed(), ... Released() の4つ。WindowListener のメソッドはその他に windowActivated(), ...Deactivated(), ...Opened(), ...Closing(), ...Iconified(), ...Deiconified() の6つがある。
実行時のデータ入力の一つとして、マウスを使う方法がある。マウスのボタンのアップダウンや位置情報を利用して動的な処理ができる。電子版のキーボードを表示させて、それをクリックさせることにより、文字情報を入力するなどの方法もある。
マウスイベントについてまとめる。静的な動き(ボタンを押す、離す、クリックする、ある領域に入る、ある領域から出る)については MouseListener、動的な動き(マウスを動かす、ボタンを押しながら動かす)については MouseMotionListener が対応する。まとめるとつぎのようになる。
| イベント | メソッド | リスナー | アダプター |
|---|---|---|---|
| マウスボタンがクリックされた時 | mouseClicked |
MouseListener |
MouseAdapter |
| マウスボタンが押されたとき | mousePressed |
||
| マウスボタンが放されたとき | mouseReleased |
||
| マウスがある領域に入ったとき | mouseEntered |
||
| マウスがある領域から離れたとき | mouseExited |
||
| マウスがドラッグされているとき | mouseDragged |
MouseMotionListener |
MouseMotionAdapter |
| マウスが動いたとき | mouseMoved |
||
| マウスホイールが動いた | mouseWheelMoved |
MouseWheelListener |
イベント処理プログラムを作るには、リスナーを実装( implements )し、addMouseListener() あるいは addMouseMotionLitener(), メソッドでイベントを受け取る場所を設定し、メソッドの定義を与える。イベントの詳細な情報は addMouseWheelLitener() MouseEvent クラスのメソッドを参照すれば良い。たとえば、マウスの位置は getX(), getY() で知ることができる。
リスナーを実装すると、すべてのメソッドを定義しなければいけなくなるので、不要なものが多い場合は Adapter を使うのが普通。Adapterはクラスなので、必要なメソッドだけオーバーライドすればよい。
次は、数字入力用のキーボードを表示し、クリックしたキーボードの数字を表示している。MyMouseAdapter が MouseAdapter を継承したサブクラスで、内部クラスとして定義している。new MyMouseAdapter() を addMouseListener の引数とすることで、マウスクリック時の処理プログラムを組み込んでいる。this はこの命令文のあるクラス、という意味である。
public class MyMouseEvent extends Frame {
Graphics gr;
MyMouseEvent() {
this.setSize(550, 300);
this.addMouseListener(new MyMouseAdapter()); // MouseListener 利用宣言
this.setVisible(true);
gr = this.getGraphics(); // 数字盤の表示
for(int i=0; i<10; i++) {
gr.drawString(""+i, 50*i+40, 150);
gr.drawRect(50*i+20, 100, 50, 80);
}
}
public static void main(String[] args) {
new MyMouseEvent();
}
class MyMouseAdapter extends MouseAdapter { // クリック時の処理プログラム
public void mouseClicked(MouseEvent me) {
System.out.println((int)((me.getX()-20)/50) + " がクリックされた");
}
}
}
ここではクリックされた時のイベントのみに反応する mouseClicked() メソッドだけを上書きしている。me.getX(), me.getY() はクリックされた点の x, y 座標を取り出す。
実行して表示される数字のどれかをクリックすると、その数字がコンソールに表示される。
次は、マウスが動くとその横位置の軌跡を時系列で表示し、ドラッグすると追随する線を描くというプログラム。MouseMotionListener の2つのメソッドを使っているので、インターフェースを実装し、2つのメソッドの定義を書いている。addMouseMotionListener の引数は this である。
public class MyMouseEvent2 extends Frame implements MouseMotionListener {
Graphics gr;
int x=0, y=0;
MyMouseEvent2() {
this.setSize(400, 400);
this.addMouseMotionListener(this); // 処理プログラムはここで定義されている
this.setVisible(true);
gr = this.getGraphics();
}
public static void main(String[] args) {
new MyMouseEvent2();
}
public void mouseMoved(MouseEvent me) {
gr.copyArea(0, 0, 400, 400, 0, 5); // 画面全体を下にずらしている
gr.clearRect(0, 0, 400, 5);
gr.fillOval(me.getX(), 0, 5, 5);
}
public void mouseDragged(MouseEvent me) {
gr.drawLine(x, y, me.getX(), me.getY()); // 直前の点と現在の点を結ぶ
x = me.getX();
y = me.getY();
}
}
copyArea() は指定された長方形をコピーし、指定された座標を左上の点としてペーストする。 clearRect() は指定の長方形を地の色に塗りつぶす。
実行時に、ボタンがクリックされたら指定されたプログラムを実行する、という場合に必要な処理プログラムを述べる。ボタンをクリックするというイベントを監視するのは ActionListener なので、それを実装し、actionPerformed() メソッドでその処理プログラムを定義する。このとき、イベントの情報は ActionEvent クラスのメソッドを使って引き出すことができる。
(1) class ... implements ActionListener { // ActionListener の実装
(2) Button mb = new Button("test"); // Button の定義と貼り付け
mb.addActionListener(new MyActionListener()); // イベント処理プログラム組み込み
(3) class MyActionListener implements ActionListener {
public void actionPerformed(ActionEvent ae){
if(ae.getActionCOmmand() == "test") {
ここに処理プログラムを記述
}
}
}
getActionCommand() はクリックされたボタンのラベルを取り出す ActionEvent のメソッドである。
public class MyButton2 extends Frame implements ActionListener {
Button btn;
MyButton2() {
this.setSize(400, 400);
btn = new Button("change");
btn.addActionListener(this);
this.add(btn, "North");
this.setVisible(true);
}
public static void main(String[] args) {
new MyButton2();
}
public void actionPerformed(ActionEvent ae) {
int j = (int)(256 * Math.random());
this.setBackground(new Color(j,j,0));
}
}
実行処理過程でエラーが生じた場合は、通常、エラーメッセージが発行されて実行が止まる。しかし、そのメッセージを受け止めて対処するプログラムを書いておけば、実行を継続させることが出来る。その仕組みが「try ... catch」構文である。
エラーの起きる可能性のある処理(この場合は「Thread.sleep()」を「try{ }」 で囲み、エラーが起きたときの処理を「catch( ) { }」に書く。生じたエラーの内容は「Exception ex」によって知ることが出来る。
例えば、スレッドの処理で発生するエラーをチェックするために、次のようなプログラム を書く。
while(true) {
repaint();
try{
Thread.sleep(100);
} catch(Exception ex) {
System.out.println("エラー割り込みが発生しました:"+ex);
}
}