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