Androidアプリでアニメーションを作ってみよう!
以前、【夏休み特別企画】として、「Androidアプリ開発を体験しよう」というブログを書きました。
中でも、3日目のアプリが、「簡単なソースのわりに楽しい」と好評だったので、そんなミニアプリを、また作ってみました。
今回は、アニメーションです。
動かす画像は、またしてもドロイドくんです。
このロボット、実は、正式名称は無いそうですが(方波見さんのエントリー「えっ!Droidじゃないの?『残念ながら、Android のロボットについては正式名称はありません。』byグーグル・広報部ご担当者さま。」参照)、
ここでは、「ドロイドくん」と呼び続けることにします。
このスケボーに乗っているドロイドくんを動かしてみましょう。
ドロイドくんが、画面の端まで移動すると、1段下がって、向きを反転するようなアニメーションを作成します。
図にすると、動きはこんな感じです。
まず、画像の準備です。
左向きと右向きのドロイドくんを用意します。

pngファイルを、
droid1.png
droid2.png
という名前で保存しておきます。
eclipseを起動し、ソースを自動生成します。
Project name: Droid Anime
Build Target Android 1.6 (Android 1.6のところをチェック)
Application name: Droid Anime
Package name: com.example.hellodroid
Create Activity: DroidAnime
Min SDK Version: 4
eclipseのworkspaceの下に、「Droid Anime」というフォルダが出来ていますので、
その下の
\res\drawable-mdpi
に作成したpngファイルをおきます。
ソースは次のように変更します。赤字の部分を追加、修正してください。
たくさんあって、大変そうですが、/** */ で囲まれたところや、 // の後に書かれているコメントを除けば、そんなに量は多くありません。頑張りましょう。
package com.example.droidanime;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
public class DroidAnime extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DroidAnimeView myView
= new DroidAnimeView( getApplication() );
setContentView(myView);
}
}
/**
* 描画用のクラス
*/
class DroidAnimeView extends View {
private Paint myPaint = new Paint();
private Bitmap myBitmap1, myBitmap2;
private int displayWidth, displayHeight;
private int imageWidth, imageHeight;
private int x, y;
private int dx, dy;
private boolean isAttached;
private static final long DELAY_MILLIS = 60;
/**
* コンストラクタ
*/
public DroidAnimeView(Context c) {
super(c);
// イベントが取得できるようにFocusを有効にする
setFocusable(true);
Resources res = this.getContext().getResources();
// 画像の読み込み
myBitmap1 = BitmapFactory.decodeResource(res,
R.drawable.droid1);
myBitmap2 = BitmapFactory.decodeResource(res,
R.drawable.droid2);
// 画像サイズの取得
imageWidth = myBitmap1.getWidth();
imageHeight = myBitmap1.getHeight();
// 座標、差分の初期化
x = 0;
y = 0;
dx = 2;
dy = imageHeight;
}
/**
* 描画処理
*/
protected void onDraw(Canvas canvas) {
Bitmap myBitmap;
// 画面を白色にする
canvas.drawColor(Color.WHITE);
// 移動方向のチェック
if ( dx < 0 ) {
// 左向きの画像
myBitmap = myBitmap1;
} else {
// 右向きの画像
myBitmap = myBitmap2;
}
// 画像の描画
canvas.drawBitmap(myBitmap, x, y, myPaint);
}
/**
* サイズが変更された時(縦から横モードになった時)に
* 呼び出される
*/
protected void onSizeChanged(int w, int h,
int oldw, int oldh) {
// 画面の縦と横のサイズを得る
displayWidth = w;
displayHeight = h;
}
/**
* 移動処理
*/
private void move() {
// x方向の画面の端まで移動したかのチェック
if (x < 0 || x + imageWidth > displayWidth) {
// x方向の移動を反転
dx = -dx;
// y座標の移動
y += dy;
}
// y方向の画面の端まで移動したかのチェック
if (y < 0 || y + imageHeight > displayHeight) {
// y方向の移動を反転
dy = -dy;
// y座標の移動
y += dy*2;
}
// x座標の移動
x += dx;
}
/**
* タイマーハンドラー
*/
private Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (isAttached) {
// 移動処理
move();
// 再描画
invalidate();
sendEmptyMessageDelayed(0, DELAY_MILLIS);
}
}
};
/**
* WindowにAttachされた時の処理
*/
protected void onAttachedToWindow() {
isAttached = true;
myHandler.sendEmptyMessageDelayed(0, DELAY_MILLIS);
super.onAttachedToWindow();
}
/**
* WindowからDetachされた時の処理
*/
protected void onDetachedFromWindow() {
isAttached = false;
super.onDetachedFromWindow();
}
}
(ソースの右側が切れているようでしたら、ブラウザの文字サイズを「小」や「最小」にしてみてください)
できたら、[Run]メニューの[Run]で、実行します。
ドロイドくんのアニメーションが表示されたと思います。
スピードを変えたいな、と思ったら、
DELAY_MILLIS = 60
のところを、30とか、90にして、調整して再実行してみてください。
これでアニメーションはできました。
でも、ただ見てるだけだと、すぐに飽きちゃいますね。
少し、動きを加えてみましょう。
クリックすると、ドロイドくんが慌てるようにします。
慌てたとき用の画像を2つ用意しましょう。

それぞれ、
droid3.png
droid4.png
というpngファイルにして、また、
\res\drawable-mdpi
の下に置きます。
ソースは、次のように手を加えます。
ソースの赤字のところが、変更箇所です。
package com.example.droidanime;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;
import android.view.MotionEvent;
public class DroidAnime extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
DroidAnimeView myView
= new DroidAnimeView( getApplication() );
setContentView(myView);
}
}
/**
* 描画用のクラス
*/
class DroidAnimeView extends View {
private Paint myPaint = new Paint();
private Bitmap myBitmap1, myBitmap2;
private Bitmap myBitmap3, myBitmap4;
private int displayWidth, displayHeight;
private int imageWidth, imageHeight;
private int x, y;
private int dx, dy;
private int count;
private boolean isOffBalance = false;
private boolean isAttached;
private static final long DELAY_MILLIS = 60;
/**
* コンストラクタ
*/
public DroidAnimeView(Context c) {
super(c);
// イベントが取得できるようにFocusを有効にする
setFocusable(true);
Resources res = this.getContext().getResources();
// 画像の読み込み
myBitmap1 = BitmapFactory.decodeResource(res,
R.drawable.droid1);
myBitmap2 = BitmapFactory.decodeResource(res,
R.drawable.droid2);
myBitmap3 = BitmapFactory.decodeResource(res,
R.drawable.droid3);
myBitmap4 = BitmapFactory.decodeResource(res,
R.drawable.droid4);
// 画像サイズの取得
imageWidth = myBitmap1.getWidth();
imageHeight = myBitmap1.getHeight();
// 座標、差分の初期化
x = 0;
y = 0;
dx = 2;
dy = imageHeight;
// 慌てる回数の初期化
count = 0;
}
/**
* 描画処理
*/
protected void onDraw(Canvas canvas) {
Bitmap myBitmap;
// 画面を白色にする
canvas.drawColor(Color.WHITE);
// 移動方向のチェック
if ( dx < 0 ) {
// 左向きの画像
myBitmap = myBitmap1;
} else {
// 右向きの画像
myBitmap = myBitmap2;
}
// 不安定な姿勢かどうかのチェック
if ( isOffBalance ) {
count++;
// 3回に1回交互に画像を入れ替える
if ( count/3 - count/6*2 == 0 ) {
myBitmap = myBitmap3;
} else {
myBitmap = myBitmap4;
}
if ( count > 18 ) {
// 不安定な姿勢の終了
count = 0;
isOffBalance = false;
}
}
// 画像の描画
canvas.drawBitmap(myBitmap, x, y, myPaint);
}
/**
* サイズが変更された時(縦から横モードになった時)に
* 呼び出される
*/
protected void onSizeChanged(int w, int h,
int oldw, int oldh) {
// 画面の縦と横のサイズを得る
displayWidth = w;
displayHeight = h;
}
/**
* タッチイベント
*/
public boolean onTouchEvent(MotionEvent event) {
// タッチされた座標の取得
int x1 = (int)event.getX();
int y1 = (int)event.getY();
// タッチされた座標がアイコン内かどうか
if (x < x1 && x1 < x + imageWidth) {
if (y < y1 && y1 < y + imageHeight ) {
// 不安定な姿勢になる
isOffBalance = true;
}
}
return true;
}
/**
* 移動処理
*/
private void move() {
// x方向の画面の端まで移動したかのチェック
if (x < 0 || x + imageWidth > displayWidth) {
// x方向の移動を反転
dx = -dx;
// y座標の移動
y += dy;
}
// y方向の画面の端まで移動したかのチェック
if (y < 0 || y + imageHeight > displayHeight) {
// y方向の移動を反転
dy = -dy;
// y座標の移動
y += dy*2;
}
// x座標の移動
x += dx;
}
/**
* タイマーハンドラー
*/
private Handler myHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
if (isAttached) {
// 移動処理
move();
// 再描画
invalidate();
sendEmptyMessageDelayed(0, DELAY_MILLIS);
}
}
};
/**
* WindowにAttachされた時の処理
*/
protected void onAttachedToWindow() {
isAttached = true;
myHandler.sendEmptyMessageDelayed(0, DELAY_MILLIS);
super.onAttachedToWindow();
}
/**
* WindowからDetachされた時の処理
*/
protected void onDetachedFromWindow() {
isAttached = false;
super.onDetachedFromWindow();
}
}
ソースの変更が終わったら、実行してみましょう。
ドロイドくん、クリックされると、バランスを崩して、手をパタパタさせます。
この慌てぶり、かわいらしいですね。
こんな風に、ちょっと手を加えるだけで、ずいぶん楽しくなったと思います。
ソースの中身については、解説していませんが、コメントを見ると、なんとなく、どこで何をやっているかは解ると思います。
いろいろ手を加えて、自分だけのアニメーションを楽しんでみてください。