Animals_step3 - Infty Project

【Animals サンプル Step3】
張り付けた動物の上をクリックすると、それぞれの鳴き声で鳴く。
その鳴く間、一定時間(ここでは 1 秒間)画像が別のものに変わる
<アニメーションの基礎:タイマーについて>
アニメーションは、アプリケーションが指定する間、一定間隔でどんどん画像をおきかえていくもの
である。
このサンプルでは、動物画像の上をクリックすると画像を切り替え、1 秒後、もとの画像に戻す操作を
する。これは、1 秒間隔で2回の操作が必要となり、間隔も長いし操作回数が少ないが、ある意味アニメ
ーションの一種となる。
Swing でアニメーションを簡単に実現するには、
javax.swing パッケージの Timer クラスを使う。
Timer
によって一定間隔でイベントを発生させ、イベント処理をするメソッド(関数)に画像を描画しなおす
処理を記述すると、アニメーションになる。タイマーの作成方法は一般に以下のようになる。
Timer timer = new Timer(イベント発生間隔, アクションリスナ);
timer.start();
// タイマーが発動する
具体的にはアクションイベント(ActionEvent)という種類のイベントが発生する。このイベントを処理
するのはアクションリスナ(ActionListener)というインターフェースを実装(implements)したクラスで、
必ず actionPerformed というメソッドをもつ。このメソッドにイベント処理の記述を行う。
<作成手順>
今回は MainPanel.java に必要な部分を書きたすだけでよい。
-1-
MainPanel.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package animals;
import
import
import
import
import
import
import
import
import
import
java.awt.Graphics;
java.awt.Graphics2D;
java.awt.SystemColor;
java.awt.event.ActionEvent;
java.awt.event.ActionListener;
java.awt.event.MouseEvent;
java.awt.event.MouseListener;
java.io.File;
java.util.ArrayList;
java.util.List;
import
import
import
import
import
import
import
import
import
import
javax.sound.sampled.AudioFormat;
javax.sound.sampled.AudioInputStream;
javax.sound.sampled.AudioSystem;
javax.sound.sampled.Clip;
javax.sound.sampled.DataLine;
javax.swing.ImageIcon;
javax.swing.JOptionPane;
javax.swing.JPanel;
javax.swing.SwingUtilities;
javax.swing.Timer;
public class MainPanel extends JPanel
private
private
private
private
private
private
implements MouseListener, ActionListener{
int animalType = 0; // 動物の種類
Animal clickedAnimal = null;
//
List<ImageIcon> images;
//
List<ImageIcon> images2;
//
File[] audioFiles;
//
List<Animal> animals;
//
private int imgWidth = 100;
private int imgHeight = 100;
クリックされた動物
動物の画像リスト
画像2のリスト
音声ファイル
動物のリスト
// 画像幅
// 画像高さ
private int x,y; // クリックされた座標の一時保管用フィールド
private
private
private
private
final String IMG = "img";
// 描画モードその1
final String IMG2 = "img2"; // 描画モードその2
String imgType = IMG; // 描画する画像の種類 int や boolean でも実装可能
Timer timer;
/**
* コンストラクタ
*/
public MainPanel() {
super();
// マウスイベントを受け取れるようにする
addMouseListener(this);
// 初期化
/* List は直接 new できず、リストの種類を指定する必要がある。
* 普通に使う List は ArrayList で new すれば問題ない。*/
images = new ArrayList<ImageIcon>();
images2 = new ArrayList<ImageIcon>();
animals = new ArrayList<Animal>();
// タイマー生成 new Timer(ミリ秒でイベント発生間隔, アクションリスナ)
/* 新たに ActionListener を implements したので
* 自分自身をアクションリスナとして登録できる */
timer = new Timer(1000, this);
// タイマーが発動するまでの遅延時間を設定(ミリ秒)
timer.setInitialDelay(100);
}
-2-
MainPanel.java
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* ファイル数に応じて各インスタンスを初期化
* new したあと必ず呼ぶ
* @throws MyException
*/
public void init() throws MyException {
// img ディレクトリ、img2 ディレクトリを取得
/* File クラスはディレクトリを含むファイルの概念を表す
* 相対パスで書くことができる */
File imgDir = new File("img");
File img2Dir = new File("img2");
// img ディレクトリにあるファイルを配列にして全て取り出す
File imgFiles[] = imgDir.listFiles();
File img2Files[] = img2Dir.listFiles();
// ファイルの数が合わなかったら Exception
if (imgFiles.length != img2Files.length)
throw new MyException("画像ファイルの数が合いません");
// List に画像オブジェクトを追加
/* getAbsolutePath 関数:ファイルの絶対パスを String で返す*/
for (int i=0; i<imgFiles.length; i++) {
images.add(new ImageIcon(imgFiles[i].getAbsolutePath()));
images2.add(new ImageIcon(img2Files[i].getAbsolutePath()));
}
// サウンドファイルを sounds ディレクトリから取り出す
audioFiles = (new File("sounds")).listFiles();
// 画像ファイルとサウンドファイルの数が合わなかったら Exception を発生させる
if (imgFiles.length != audioFiles.length)
throw new MyException("音声ファイルの数が合いません");
}
/**
* 描画メソッド<br>
* repaint 関数から呼ばれる
*/
@Override
public void paintComponent(Graphics g) {
/* パネルにボタンなどが配置されているときのため
* 今回は実際に意味はないが、慣例として忘れないよう書いておく*/
super.paintComponents(g);
// より様々な描画方法が可能になるようにキャスト
Graphics2D g2d = (Graphics2D)g;
// システムカラー(背景色)を設定
g2d.setColor(SystemColor.control);
// 画面をいったんクリア
g2d.fillRect(0, 0, this.getWidth(), this.getHeight());
// 動物描画
/* Java5 からは可変長配列 List のループを以下ように書くことができる
* animal には animals リストから順にとった Animal オブジェクトがはいる */
for (Animal animal : animals) {
// 画像を描画
/* drawImage(画像オブジェクト, x 座標, y 座標, 幅, 高さ, ImageObserver)
* ImageObserver は画像のロードを通知するもの。たいていは this でよい */
g2d.drawImage(images.get(animal.getAnimalType()).getImage(),
animal.getCenterPoint().x-animal.getSize().width/2,
animal.getCenterPoint().y-animal.getSize().height/2,
animal.getSize().width, animal.getSize().height, this);
}
// 描画モードが"img"だったらタイマーで画像切り替えの必要はなくなる
if (imgType.equals(IMG)) {
// タイマーが動いていたらストップ
if (timer.isRunning())
timer.stop();
-3-
MainPanel.java
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
}
// 描画モードが"img2"だったら鳴いてる動物の画像を描画しなおす
else {
// 画像その2を描画
g2d.drawImage(images2.get(clickedAnimal.getAnimalType()).getImage(),
clickedAnimal.getCenterPoint().x-clickedAnimal.getSize().width/2,
clickedAnimal.getCenterPoint().y-clickedAnimal.getSize().height/2,
clickedAnimal.getSize().width,
clickedAnimal.getSize().height, this);
}
}
/**
* マウスクリックで呼ばれる関数
*/
@Override
public void mouseClicked(MouseEvent e) {
// クリックされた座標を取得
x = e.getX();
y = e.getY();
int onImageIndex = 0;
// 画像上クリックなら index(動物の種類),そうでなければ ListSize
/* animals の中の Animal オブジェクトを順に見て、クリックされた場所が
* 動物画像の中に含まれるかどうかをみていく*/
for (Animal animal : animals) {
// 動物画像の中でクリックされていたらループから抜ける
if (animal.contains(x, y))
break;
onImageIndex ++;
}
// 動物画像の中でクリックされていたら鳴き声を再生
if (onImageIndex < animals.size()) {
try {
// クリックされた動物を取得
clickedAnimal = animals.get(onImageIndex);
// タイマーをスタートしてアクションイベントを発生させる
// すると、actionPerformed メソッドが呼ばれる
timer.start();
// 動物の種類に応じた鳴き声を再生
play(clickedAnimal.getAnimalType());
} catch (MyException ex) {
// タイマーをとめて画像の描画モードを初期化
timer.stop();
imgType = IMG;
// エラーをダイアログ表示
// JOptionPane.showMessageDialog(フレーム,メッセージ,タイトル,アイコンの種類)
JOptionPane.showMessageDialog(SwingUtilities.getWindowAncestor(this),
ex.getMessage(), "再生エラー発生", JOptionPane.ERROR_MESSAGE);
// 上と合わせて 1 行
}
}
// 動物画像の外でクリックされていたら画像を表示
else {
// Animal オブジェクトを生成
Animal animal = new Animal(animalType, imgWidth, imgHeight);
// 中心座標を設定
animal.setCenterPoint(x, y);
// リストに追加
animals.add(animal);
// 再描画
repaint();
}
}
-4-
MainPanel.java
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
/**
* 音声再生メソッド
* @param index
* @throws Exception
*/
private void play(int index) throws MyException {
try {
// オーディオ入力ストリームを取得
AudioInputStream audioStream = AudioSystem.getAudioInputStream(audioFiles[index]);
// オーディオ形式を取得
// AU、AIFF、WAVE 形式に対応している
AudioFormat format = audioStream.getFormat();
// データラインの情報(フォーマットなど)を作成
DataLine.Info di = new DataLine.Info(Clip.class, format);
// 情報に基づいてラインを取得
// Clip はデータをメモリ上に読み込んでおいてから再生する
Clip clip = (Clip) AudioSystem.getLine(di);
// ラインを開く
clip.open(audioStream);
// ラインでのデータ入出力を可能にする
clip.start();
// ラインからキューに入っているデータを排出
clip.drain();
} catch (Exception e) {
throw new MyException("再生失敗:"+e.getMessage(), e);
}
}
/*
*
*
*
MouseListener を implements しているため、
mouseClicked・mouseEntered・mouseExited・mousePressed・mouseReleased の
4つとも記述しなければならないが、このサンプルで使うのは
mouseClicked だけなので後は空でよい。 */
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
/**
* Timer によるイベント発生時の実行メソッド
*/
@Override
public void actionPerformed(ActionEvent e) {
// 画像の描画モード"img"と"img2"をきりかえ
if (imgType.equals(IMG)) {
imgType = IMG2;
} else {
imgType = IMG;
}
// 描画
repaint();
}
-5-
MainPanel.java
253
254
255
256
257
258
259
260
261
/**
* 動物の種類 setter
* @param aimalType
*/
public void setAnimalType(int animalType) {
this.animalType = animalType;
}
}
-6-