androidimageviewandroid-animationobjectanimatoranimatorset

Animating an ImageView with a "V" shaped animation


I'm trying to move an image from right to left doing a "V" (up-down-up). Here's an image describing what I'm after: V shaped animation

I tried using ObjectAnimator and AnimatorSet but I'm not getting what I was hopping to get. And it's hard to understand why I'm getting something else.
Here's my current code:

/**
 * translateLeft = -160dp
 * translateDown = 25dp
 * translateUp   = -25dp
 */
private void vShapedAnimation () {
    AnimatorSet upDownSet = new AnimatorSet();
    AnimatorSet downUpSet = new AnimatorSet();
    AnimatorSet finalSet = new AnimatorSet();
    ObjectAnimator rightToLeft = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_X, this.getResources().getDimensionPixelOffset(R.dimen.translateLeft) / 2);
    ObjectAnimator upDown = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_Y, this.getResources().getDimensionPixelOffset(R.dimen.translateDown));
    ObjectAnimator downUp = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_Y, this.getResources().getDimensionPixelOffset(R.dimen.translateUp));
    upDownSet.playTogether(
            rightToLeft,
            upDown
    );
    downUpSet.playTogether(
            rightToLeft,
            downUp
    );
    finalSet.playSequentially(
            upDownSet,
            downUpSet
    );
    finalSet.setDuration(300);
    finalSet.addListener(new Animator.AnimatorListener() {
        @Override
        public void onAnimationStart(Animator animation) {

        }

        @Override
        public void onAnimationEnd(Animator animation) {
            animation.removeListener(this);
            animation.setDuration(0);
            animation.setInterpolator(new ReverseInterpolator());
            animation.start();
        }

        @Override
        public void onAnimationCancel(Animator animation) {

        }

        @Override
        public void onAnimationRepeat(Animator animation) {

        }
    });
    finalSet.start();
}

The ReverseInterpolator in the AnimatorListener comes from here:

How to reset ObjectAnimator to it's initial status?

I'm planning to rotate the image a bit at the same time. And if the "V" could be curved inward a tiny bit, that would be perfect. But if, at first, someone could help me do the basic animation, it would be deeply appreciated.


Solution

  • ObjectAnimator has its weird moments, but I finally got the hang of it (at least enough to solve my own problem).

    The catch is that, in a sequence of animations, it is better to always indicate the starting position as well as the ending position. In the question I wrote, the three ObjectAnimators were only given the ending position. That makes the animation start in weird places.

    Using a pseudo-code example, if you want to go from A to C going through B, you need to write it down as so:

    ObjectAnimator AtoB_X = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_X, A.x, B.x - A.x);
    ObjectAnimator BtoC_X = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_X, B.x - A.x, C.x - A.x);
    ObjectAnimator AtoB_Y = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_Y, A.y, B.y - A.y);
    ObjectAnimator BtoC_Y = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_Y, B.y - A.y, C.y - A.y);
    

    IMPORTANT: always use dp measurements. Never use px measurements. That is why, in my code, I use this.getResources().getDimensionPixelOffset(). It converts the dp value to a px value appropriate to each screen resolution.

    Now, for my working code:

    private void vShapedAnimation() {
        AnimatorSet upDownSet = new AnimatorSet();
        AnimatorSet downUpSet = new AnimatorSet();
        AnimatorSet finalSet = new AnimatorSet();
        ObjectAnimator rightToHalf = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_X, 0, this.getResources().getDimensionPixelOffset(R.dimen.translateLeft) / 2);
        ObjectAnimator halfToLeft = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_X, this.getResources().getDimensionPixelOffset(R.dimen.translateLeft) / 2, this.getResources().getDimensionPixelOffset(R.dimen.translateLeft));
        ObjectAnimator upDown = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_Y, 0, this.getResources().getDimensionPixelOffset(R.dimen.translateDown));
        ObjectAnimator downUp = ObjectAnimator.ofFloat(this.imageView, View.TRANSLATION_Y, this.getResources().getDimensionPixelOffset(R.dimen.translateDown), 0);
        upDownSet.playTogether(
                rightToHalf,
                upDown
        );
        downUpSet.playTogether(
                halfToLeft,
                downUp
        );
        finalSet.playSequentially(
                upDownSet,
                downUpSet
        );
        finalSet.setInterpolator(new LinearInterpolator());
        finalSet.setDuration(300);
        finalSet.start();
    }