LoginSignup
27
36

More than 5 years have passed since last update.

ObjectAnimator を使って View の表示切り替えをかっこよくしてみる

Posted at

はじめに

ビューの切り取り位置を少しずつ変化させながら表示、非表示を切り替え行うアニメーションを紹介します。

GitHub のサンプルプロジェクトはこちら。
https://github.com/suzukihr/ClipAnimation

参考ドキュメント
http://developer.android.com/intl/ja/guide/topics/graphics/prop-animation.html

clip_animation.gif

アニメーションの定義

まず下記ようなインタフェースを用意します。createPath メソッドは progress (0-1) と View のサイズからどこの位置を切り取るかを返します。

ClipAnimation.java
public interface ClipAnimation {
    Path createPath(float progress, int width, int height);
}

サンプルでは createPath を実装したクラスを4つほど作りました。

CircleClipAnimation.java
public class CircleClipAnimation implements ClipAnimation {
    @Override
    public Path createPath(float progress, int width, int height) {
        float radius = (float) (Math.sqrt(width * width + height * height) / 2f);
        float centerX = width / 2f;
        float centerY = height / 2f;

        Path path = new Path();
        path.addCircle(centerX, centerY, radius * progress, Path.Direction.CCW);
        return path;
    }
}
LeftToRightClipAnimation.java
public class LeftToRightClipAnimation implements ClipAnimation {
    @Override
    public Path createPath(float progress, int width, int height) {
        Path path = new Path();
        path.addRect(0, 0, width * progress, height, Path.Direction.CCW);
        return path;
    }
}
TopToBottomClipAnimation.java
public class TopToBottomClipAnimation implements ClipAnimation {
    @Override
    public Path createPath(float progress, int width, int height) {
        Path path = new Path();
        path.addRect(0, 0, width, height * progress, Path.Direction.CCW);
        return path;
    }
}
DroidClipAnimation.java
public class DroidClipAnimation implements ClipAnimation {
    @Override
    public Path createPath(float progress, int width, int height) {
        float radius = (float) (Math.sqrt(width * width + height * height) / 2f);
        float leftEyeX = width * 62.8f / 192;
        float leftEyeY = height * 73.5f / 192;
        float rightEyeX = width * 128.8f / 192;
        float rightEyeY = leftEyeY;

        Path path = new Path();
        path.addCircle(leftEyeX, leftEyeY, radius * progress, Path.Direction.CCW);
        path.addCircle(rightEyeX, rightEyeY, radius * progress, Path.Direction.CCW);
        return path;
    }
}

使い方

アニメーション用のクラスを用意します。ポイントは onDraw 内の Canvas#clipPath の部分です。

ClipAnimationImageView.java
public class ClipAnimationImageView extends ImageView {

    // animation type
    private ClipAnimation clipAnimation;

    // progress for animation
    private float progress = 1f;

    public ClipAnimationImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (clipAnimation != null) {
            canvas.clipPath(clipAnimation.createPath(progress, getWidth(), getHeight()));
        }
        super.onDraw(canvas);
    }

    /**
     * getter for ObjectAnimator
     */
    public float getProgress() {
        return progress;
    }

    /**
     * setter for ObjectAnimator
     */
    public void setProgress(float progress) {
        this.progress = progress;
    }

    public void show(ClipAnimation clipAnimation) {

        this.clipAnimation = clipAnimation;

        ObjectAnimator animator = ObjectAnimator.ofFloat(this, "progress", 0, 1f);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                setVisibility(View.VISIBLE);
            }
        });
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                invalidate();
            }
        });
        animator.start();
    }

    public void hide(ClipAnimation clipAnimation) {

        this.clipAnimation = clipAnimation;

        ObjectAnimator animator = ObjectAnimator.ofFloat(this, "progress", 1f, 0);
        animator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                setVisibility(View.INVISIBLE);
            }
        });
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                invalidate();
            }
        });
        animator.start();
    }
}

あとは下記のように show, hide メソッドを呼べばオッケーです。

clipAnimationImageView.show(new CircleClipAnimation());
clipAnimationImageView.hide(new DroidClipAnimation());
27
36
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
27
36