Canvas#setMatrixを使ったアニメーション

画像を変形したり、図形を回転させたりする際には

Canvas#setMatrixを使ったほうが便利なことがある。

例えば、星型の図形を回転させるアニメーションを作りたいとする。

手順は以下のようになる。
(1) 事前に星の図形(Path)を作っておく。
(2) 回転角を求める。
(3) 回転行列を作成する。
(4) Canvas#setMatrixする。
(5) Canvas#drawPath

高度なことをするにはOpenGL ESを使う必要があるが、
簡単な図形であれば、手軽に変形できるので便利。

完成すると以下のような図形がくるくる回転する。

20110519_and

・・・そして下のが実際のコード。
■Activity
package net.darkdrive.android.sample.matrix;

import android.app.Activity;
import android.os.Bundle;

public class SampleMatrixActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new SampleMatrixView(this, null));
}
}

■View
package net.darkdrive.android.sample.matrix;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.FloatMath;
import android.view.View;

public class SampleMatrixView extends View
{
// Paint
private final Paint paint;

// 星の図形
private final Path star;

public SampleMatrixView(Context context, AttributeSet attr) {
super(context, attr);

// 背景色は黒
setBackgroundColor(Color.BLACK);

// Paint
paint = new Paint();

// (1) 事前に星の図形(Path)を作っておく。
star = new Path();
star.moveTo(0.0f, -1.0f); // 大きさは半径1の円に収まる大きさにしておく
for ( int i = 1; i <= 10; i++ )
{
float rad = 2 * (float)Math.PI * i / 10;
float len = (i%2==0)?1.0f:0.5f;
star.lineTo(FloatMath.sin(rad)*len, -FloatMath.cos(rad)*len);
}

// 再描画タイマーを作成
Message msg = timerHandler.obtainMessage(INVALIDATE);
nextTime = SystemClock.uptimeMillis();
timerHandler.sendMessageAtTime(msg, nextTime);
}

private static final int INVALIDATE = 1;
private long nextTime;

private static final int INTERVAL = 1000/20; //20fps

private int frameCount = 0;

// タイマーイベント
private final Handler timerHandler = new Handler() {
@Override
public void handleMessage(Message msg){
if ( msg.what == INVALIDATE )
{
// (2) 回転角を求める。
frameCount++;

//画面を再描画
invalidate();

msg = obtainMessage( INVALIDATE );
long current = SystemClock.uptimeMillis();
if ( nextTime < current )
{
nextTime = current + INTERVAL;
}
sendMessageAtTime(msg,nextTime);

nextTime += INTERVAL;
}
}
};

/**
* 描画
*/
@Override
protected void onDraw(Canvas canvas)
{
Rect clip = canvas.getClipBounds();
int width = clip.right;
int height = clip.bottom;

Matrix project = new Matrix(); // 単位行列を生成

// (3) 回転行列を作成する。
project.postRotate(frameCount); // 回転
project.postScale(250.0f, 250.0f); // 縦横250倍に拡大
project.postTranslate(width/2.0f, height/2.0f); // 画面中央寄せ
project.postConcat(canvas.getMatrix()); // 元の描画位置を加算する

// (4) Canvas#setMatrixする。
canvas.setMatrix(project);

// (5) Canvas#drawPath
paint.setStyle(Paint.Style.FILL);
paint.setColor(Color.argb(255,255,255,0));
canvas.drawPath(star, paint);
}
}

・・・・投影用の行列を書き換えただけじゃん!と言われたら、ぐうの音も出ないですが。。。

2011.5.19
NECO

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA

*