Unmotivated

やる気はない

Android のアニメーションを自作する

Android で独自定義のカスタムアニメーションを作りたかったので試してみたところ、 Animation クラスを継承して割と簡単に作れたのでメモ。

ここでは例として、 View を円周に沿って動かすようなアニメーションを作ってみました。
アニメーションさせたい View に対して中心点を指定して、開始角度と終了角度を与えてアニメーションさせてみます。

android-arc-translate-animation.png

1
2
3
4
5
6
7
8
9
10
11
/**
 * 円弧上に沿うように移動させるアニメーション
 * @param startDegrees  開始角度
 * @param endDegrees    終了角度
 * @param centerXType   中心点のX座標のタイプ
 * @param centerXValue  中心点のX座標を表す値
 * @param centerYType   中心点のY座標のタイプ
 * @param centerYValue  中心点のY座標を表す値
 */
public ArcTranslateAnimation(int startDegrees, int endDegrees,
        int centerXType, float centerXValue, int centerYType, float centerYValue);

コンストラクタ

今回はパラメータを受け取るだけ。

1
2
3
4
5
6
7
8
9
public ArcTranslateAnimation(int startDegrees, int endDegrees,
        int centerXType, float centerXValue, int centerYType, float centerYValue) {
    this.mStartRad = (float)(startDegrees * Math.PI / 180.0f);
    this.mEndRad   = (float)(endDegrees   * Math.PI / 180.0f);
    this.mCenterXType  = centerXType;
    this.mCenterXValue = centerXValue;
    this.mCenterYType  = centerYType;
    this.mCenterYValue = centerYValue;
}

initialize()

まず initialize() で View の座標やサイズなどの情報を取得します。

他のアニメーションに倣い、回転の中心点を指定するときに絶対的(Animation.ABSOLUTE)か相対的(Animation.RELATIVE_TO_SELF)か、親基準で相対的(Animation.RELATIVE_TO_PARENT)かを選べるようにしてみましたが、このあたりは resolveSize() メソッドを使うことで容易に座標に変換できます。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Override
public void initialize(int width, int height, int parentWidth,
        int parentHeight) {
    super.initialize(width, height, parentWidth, parentHeight);

    float fromX   = resolveSize(Animation.ABSOLUTE, 0, width, parentWidth);
    float fromY   = resolveSize(Animation.ABSOLUTE, 0, height, parentHeight);
    float centerX = resolveSize(mCenterXType, mCenterXValue, width, parentWidth);
    float centerY = resolveSize(mCenterYType, mCenterYValue, height, parentHeight);

    mDeltaRad = (float)Math.atan2(fromY - centerY, fromX - centerX);
    mRadius = (float)Math.sqrt(Math.pow(fromX - centerX, 2) + Math.pow(fromY - centerY, 2));
    mStartPoint = new Point((int)(fromX - centerX), (int)(fromY - centerY));
}

中心点の座標がとれたので、開始地点と中心点における初期角度、円の半径、開始点などを保存しておきます。

applyTransformation()

applyTransformation() がアニメーションのキモで、引数として渡される interpolatedTime パラメータに 0.0f – 1.0f でアニメーションの進行値が入ってくるので、その値に合わせて表示を変更してやります。 ちなみに interpolatedTime は Interpolator が適用された値が設定されているようなので、与えられた値をそのまま進行の割合として信じれば良さそうです。

表示を変更するには、同じく引数として渡される t パラメータに対して操作を行えば OK です。 位置やサイズを変更するなら getMatrix() で参照できる Matrix インスタンスを操作し、アルファ値を変更するなら setAlpha() でアルファ値を設定します。

1
2
3
4
5
6
7
8
9
10
11
12
private PointF getArcPoint(float interpolatedTime) {
    float rad = mStartRad + (mEndRad - mStartRad) * interpolatedTime + mDeltaRad;
    float dx = (float)(Math.cos(rad) * mRadius);
    float dy = (float)(Math.sin(rad) * mRadius);
    return new PointF(dx - mStartPoint.x, dy - mStartPoint.y);
}

@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
    PointF p = getArcPoint(interpolatedTime);
    t.getMatrix().postTranslate(p.x, p.y);
}

今回は Translate アニメーションなので、 getArcPoint() で座標を決定したら Matrix#postTranslate() で位置を移動してやります。

カスタムアニメーションを使用する

これで普通の Animation と同じように使うことができます。

使用例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
View target = findViewById(R.id.targetView);

Animation anim1 = new ArcTranslateAnimation(0, 90, Animation.ABSOLUTE, -300, Animation.ABSOLUTE, 0);
anim1.setDuration(1000);
anim1.setFillEnabled(true);
anim1.setFillAfter(true);
target.startAnimation(anim1);

Animation anim2 = new ArcTranslateAnimation(90, 0, Animation.ABSOLUTE, -300, Animation.ABSOLUTE, 0);
anim2.setStartOffset(1000);
anim2.setDuration(1000);
anim2.setFillEnabled(true);
anim2.setFillAfter(true);
target.startAnimation(anim2);

もちろん AnimationSet で使うこともできます。

ArcTranslateAnimation クラス全容

最後に今回作成した ArcTranslateAnimation クラスを置いておきます。

Comments