前回やったように,ボタンのような GUI部品に対するユーザ入力は,GUI部品 に関連づけられた Event Listenerによっておこなわれるが,ボタンなどの部 品に関しては,MouseEventのような低レベルのイベントではなく, ActionEventというイベントで処理する.
ボタンのような GUI部品では,mousePressを受け取ってActionEventに変換す るだけだが,テキストフィールドのようなものでは,キー入力やマウスを使っ た編集作業は内部で処理して,リターンキーが押された時に始めて, ActionEventを発生するようになっている.
また,チェックボックスのように,イベントを発生させるよりは,状態を内 部で保持しておいて,他のGUI部品を利用したイベント発生時に内部をチェッ クする使い方が主だというGUI部品もある.ただし,チェックボックスの内部 状態が変化した時に,ItemEventイベントを発生するので,それを受け取る Event Listenerを作っておけば,イベントを捕まえることができる.
import java.awt.*;
import java.awt.event.*;
public class GUITest2 extends Frame implements ActionListener,MouseListener{
// 画面に表示するメッセージの変数.空文字列 "" で初期化する必要がある.
String message="";
Button button1, button2, button3;
Checkbox check1, check2;
TextField textField;
GUITest2(){
setLayout(new FlowLayout());
// 「Check1」というラベルを持つチェックボックスを作り,window中に配置
add(check1=new Checkbox("Check1"));
// 「Button1」というラベルを持つボタンを作り,window中に配置
add(button1=new Button("Button1"));
button1.addActionListener(this);
// ラベルを持たないチェックボックスを作り,window中に配置
add(check2=new Checkbox());
// ラベルを持たないボタンを作り,window中に配置
add(button2=new Button("Button2"));
button2.addActionListener(this);
// 幅10文字分のTextField(テキスト入力用領域)を作り,window中に配置
add(textField=new TextField(10));
textField.addActionListener(this);
add(button3=new Button("Quit"));
button3.addActionListener(this);
// windowのサイズを 500 x 500 に変更
setSize(500,500);
// windowを表示する.
setVisible(true);
addMouseListener(this);
}
public static void main(String args[]){
// GUITest2クラスのインスタンスを生成
GUITest2 window=new GUITest2();
// window内部に GUI部品を置くときのレイアウトをFlowLayoutに指定
}
public void paint(Graphics g){
// 以後の描画,塗り潰しの色を黄色に指定
g.setColor(Color.yellow);
// (0,0)から幅500,高さ500で描画
g.fillRect(0,0,500,500);
// 以後の描画,塗り潰しの色を黒に指定
g.setColor(Color.black);
// 文字列 message の内容を(50,100)の座標から描画
g.drawString(message,50,100);
}
// 内部の GUI 部品以外のところでマウスが押された時に呼ばれる.
public void mousePressed(MouseEvent e){
int mx=e.getX(), my=e.getY();
// マウスが押された時に渡される Event と押された時のX座標,Y座標を表示
System.out.println("mouseDown("+e+","+mx+","+my+")");
// イベントの処理をおこなったことを表すため true を返す
}
public void mouseReleased(MouseEvent e){}
public void mouseClicked(MouseEvent e){}
public void mouseEntered(MouseEvent e){}
public void mouseExited(MouseEvent e){}
// GUI部品がイベントを処理して,その部品を含むクラスに処理結果を渡す
// 際に呼ばれる.
public void actionPerformed(ActionEvent e){
// イベントのターゲットは GUI 部品のオブジェクトだが,
// これが Button クラスのインスタンスかどうかのチェック
System.out.println(e);
Object source=e.getSource();
if(source instanceof Button){
String label=e.getActionCommand();
// 押されたボタンのラベルが"Button1"かどうかのチェック
if(label.equals("Quit")){
System.exit(0);
}
else if(label.equals("Button1")){
// その時は表示する messageは "Button1"
message="Button1," + "Check1 is "+check1.getState();
}
else{
// それ以外の時は表示する messageは "Unknown Button"とする.
message="Button2," + "Check2 is "+check2.getState();
}
}
// イベントのターゲットが Checkbox クラスのインスタンスかどうかのチェック
else if(source instanceof Checkbox){
// その時は チェックボックスの内容を表示
message="Checkbox"+(Checkbox)source;
}
// イベントのターゲットが TextField クラスのインスタンスかどうかのチェック
else if(source instanceof TextField){
// その時は 内容を表示
message="TextField"+(TextField)source;
}
// 明示的に,書き直しをする
repaint();
}
}
実行してみると分かるが,内部のGUI部品の内側でマウスのボタンを押した時
は,外側のFrameのmouseDownは呼ばれない.
g.fillOval(ball_x,ball_y,ball_size,ball_size);
のように円で描いているが,これをイメージファイルを読み込んで,それを表
示するように変更しなさい.ボールのイメージはgimp 等で作っても良いし,
どこかから持ってきても良い.
サンプルプログラムではゲームを遊んでいるわけではないことを示すために,
g.drawString("Not a game, but a excercise in CP1(Wed/tanaka)",0,200);
という文字列を表示するようにしてあります.この表示は取り除かないで下さ
い.
import java.awt.*;
import java.awt.event.*;
class BallGame extends Frame implements KeyListener{
// ボールの座標,速度のx, y成分の宣言
int ball_x=100,ball_y=100,ball_vx=16,ball_vy=12;
// ボールの大きさ
int ball_size=20;
// 画面の幅,高さ
int width=300, height=600;
// バーの座標
int bar_x=0, bar_y=550;
// バーの速度
int bar_vx=0;
// バーの幅,高さ
int bar_width=60,bar_height=10;
// BallGameクラスのコンストラクタ
public BallGame(){
// 親クラスである Frameのコンストラクタを呼ぶ
super();
// ウィンドウのサイズを変更
setSize(width,height);
// ウィンドウを表示
setVisible(true);
addKeyListener(this);
}
public static void main(String[] args){
// BallGameクラスのインスタンスを作成
BallGame frame=new BallGame();
// スレッドを獲得
Thread t=Thread.currentThread();
for(;;){
// 0.1秒(100ミリ秒)待つ
try { t.sleep(100); }
catch(Exception e){}
// BallGameクラスのインスタンス frame のtimeTickメソッドの呼び出し
frame.timeTick();
}
}
void timeTick(){
// バーの移動
bar_x=bar_x+bar_vx;
// 左端から飛び出そうになったら左端に合わせる
if(bar_x<0) bar_x=0;
// 右端から飛び出そうになったら右端に合わせる
else if(bar_x+bar_width >width) bar_x=width-bar_width;
// ボールの移動.古い座標を保存
int old_x=ball_x;
int old_y=ball_y;
// 速度に従って,次の座標を決定
ball_x=ball_x+ball_vx;
ball_y=ball_y+ball_vy;
// 左端から飛び出そうになったら,反射させる
if(ball_x<0){
ball_x= -ball_x;
ball_vx= -ball_vx;
}
// 右端から飛び出そうになったら,反射させる
else if(ball_x >width-ball_size){
ball_x=(width-ball_size)-(ball_x-(width-ball_size));
ball_vx= -ball_vx;
}
// 上端から飛び出そうになったら,反射させる
if(ball_y<0.0){
ball_y= -ball_y;
ball_vy= -ball_vy;
}
// バーのある線を通過しそうになったら,
else if(ball_y >bar_y && old_y<=bar_y){
// バーのある線を横切るX座標を計算
int x=old_x+(ball_x-old_x)*(bar_y-old_y)/(ball_y-old_y);
// バーと接触している場合は反射させる.
if(bar_x <= x && x<=bar_x+bar_width){
ball_y=bar_y-(ball_y-bar_y);
ball_vy= -ball_vy;
}
}
// 下端から飛び出そうになったら,反射させる
else if(ball_y > height){
ball_vy=-ball_vy;
}
// 再表示
repaint();
}
public void paint(Graphics g){
// 描画色を黒にする.
g.setColor(Color.black);
// 全体を塗り潰す
g.fillRect(0,0,width,height);
// 描画色を白にする.
g.setColor(Color.white);
// 警告文字列を(0,200)の点を左下にして描く
g.drawString("Not a game, but a excercise in CP1(Wed/tanaka)",0,200);
// 描画色を赤にする.
g.setColor(Color.red);
// ボールを描く
g.fillOval(ball_x,ball_y,ball_size,ball_size);
// オフスクリーンイメージへの描画色を青にする.
g.setColor(Color.blue);
// バーを描く
g.fillRect(bar_x,bar_y,bar_width,bar_height);
}
// キーが押された時にこのメソッドが呼ばれる.
public void keyReleased(KeyEvent e){}
public void keyTyped(KeyEvent e){}
public void keyPressed(KeyEvent e){
int key=e.getKeyChar();
// 'h' のキーが押された時は,バーの移動速度を -10に
if(key=='h'){
bar_vx= -10;
}
// 'l' のキーが押された時は,バーの移動速度を 10に
else if(key=='l'){
bar_vx=10;
}
// 'j' のキーが押された時は,バーの移動速度を 0に
else if(key=='j'){
bar_vx=0;
}
else if(key=='q'){
System.exit(0);
}
}
}