オルタナティブ・ブログ > まあまあ元気になる話 >

IT業界での少し気分がアップする出来事、心持ち役立つこと、ややイイ話

Androidアプリでアニメーションを作ってみよう!

»

以前、【夏休み特別企画】として、「Androidアプリ開発を体験しよう」というブログを書きました。
中でも、3日目のアプリが、「簡単なソースのわりに楽しい」と好評だったので、そんなミニアプリを、また作ってみました。
今回は、アニメーションです。

動かす画像は、またしてもドロイドくんです。
Androids
このロボット、実は、正式名称は無いそうですが(方波見さんのエントリー「えっ!Droidじゃないの?『残念ながら、Android のロボットについては正式名称はありません。』byグーグル・広報部ご担当者さま。」参照)、
ここでは、「ドロイドくん」と呼び続けることにします。
このスケボーに乗っているドロイドくんを動かしてみましょう。
ドロイドくんが、画面の端まで移動すると、1段下がって、向きを反転するようなアニメーションを作成します。
図にすると、動きはこんな感じです。
Photo

まず、画像の準備です。
左向きと右向きのドロイドくんを用意します。
Droid1
Droid2
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]で、実行します。
ドロイドくんのアニメーションが表示されたと思います。
Photo_6

スピードを変えたいな、と思ったら、
DELAY_MILLIS = 60
のところを、30とか、90にして、調整して再実行してみてください。

これでアニメーションはできました。
でも、ただ見てるだけだと、すぐに飽きちゃいますね。

少し、動きを加えてみましょう。
クリックすると、ドロイドくんが慌てるようにします。

慌てたとき用の画像を2つ用意しましょう。
Droid3
Droid4
それぞれ、
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();  
    }
}

ソースの変更が終わったら、実行してみましょう。
ドロイドくん、クリックされると、バランスを崩して、手をパタパタさせます。
Photo_7

この慌てぶり、かわいらしいですね。
こんな風に、ちょっと手を加えるだけで、ずいぶん楽しくなったと思います。

ソースの中身については、解説していませんが、コメントを見ると、なんとなく、どこで何をやっているかは解ると思います。
いろいろ手を加えて、自分だけのアニメーションを楽しんでみてください。

Comment(2)