Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flip animation in Android for a Fragment transaction setting a "z" index or a camera

I'm trying to animate transaction between two fragments, with this code: http://developer.android.com/training/animation/cardflip.html

But the result is exactly this: http://developer.android.com/training/animation/anim_card_flip.mp4

However, I want this result: https://www.youtube.com/watch?v=52mXHqX9f3Y

The difference, is that even tho both rotate 180º, the second one does it with a different camera (Z-Axis).

So the question is:

  • Can I apply a Z-Index to object animators?
  • Or, can I provide an Animation class instead of a XML file containing an animation to animate fragment transitions?

Thanks.

Edit: Check differences. enter image description here

like image 291
Reinherd Avatar asked Aug 26 '14 13:08

Reinherd


People also ask

What are the two different types of view animation?

There are two types of animations that you can do with the view animation framework: Tween animation: Creates an animation by performing a series of transformations on a single image with an Animation. Frame animation: or creates an animation by showing a sequence of images in order with an AnimationDrawable .

What is scale animation in Android?

↳ android.view.animation.ScaleAnimation. An animation that controls the scale of an object. You can specify the point to use for the center of scaling.


1 Answers

To achieve what you want you need to do two more things in your animators:

  • rotate the view using a pivot that is not the default one (placed in the middle of the view)
  • translate the view while rotating

In both cases you need to know the size of your view, so I would recommend to create your custom layout component to be used as root of your fragments, exposing a set of properties you can animate using different objectanimator inside your xml.

The component should look like this:

public class FlippableLayout extends FrameLayout {

    private FlipEvaluator flipRightInEvaluator;
    private FlipEvaluator flipRightOutEvaluator;
    private FlipEvaluator flipLeftInEvaluator;
    private FlipEvaluator flipLeftOutEvaluator;

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

    public FlippableLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }

    private void init() {
        setCameraDistance(getCameraDistance() * 10); // reduces perspective skewing
        flipRightInEvaluator = new FlipEvaluator(
                1f, .5f, // pivotX/pivotY
                -1f, 0f, // translationX start/end
                -180, 0, // rotationY start/end
                0f, 1f); // alpha start/end
        flipRightOutEvaluator = new FlipEvaluator(
                0f, .5f,
                0f, 1f,
                0, 180,
                1f, 0f);
        flipLeftInEvaluator = new FlipEvaluator(
                .0f, .5f,
                1f, 0f,
                180, 0,
                0f, 1f);
        flipLeftOutEvaluator = new FlipEvaluator(
                1f, .5f,
                0f, -1f,
                0, -180,
                1f, 0f);
    }

    public void setFlipRightIn(float value) {
        evaluateUsing(flipRightInEvaluator, value);
    }

    public void setFlipRightOut(float value) {
        evaluateUsing(flipRightOutEvaluator, value);
    }

    public void setFlipLeftIn(float value) {
        evaluateUsing(flipLeftInEvaluator, value);
    }

    public void setFlipLeftOut(float value) {
        evaluateUsing(flipLeftOutEvaluator, value);
    }

    private void evaluateUsing(FlipEvaluator evaluator, float value) {
        float cappedValue = Math.min(1f, Math.max(0f, value));
        setPivotX(getWidth() * evaluator.getPivotX());
        setPivotY(getHeight() * evaluator.getPivotY());
        setAlpha(evaluator.getAlpha(cappedValue));
        setTranslationX(getWidth() * evaluator.getTranslationX(cappedValue));
        setRotationY(evaluator.getRotationY(cappedValue));
    }

    private static class FlipEvaluator {
        private final float pivotX;
        private final float pivotY;
        private final float startTranslationX;
        private final float endTranslationY;
        private final float startRotationY;
        private final float endRotationY;
        private final float startAlpha;
        private final float endAlpha;

        /**
         * Simple evaluator holding all the start/end values for a flip animation.
         *
         * @param pivotX value between 0 and 1, where 0 is the left border and 1 is the right border of the target
         * @param pivotY value between 0 and 1, where 0 is the top border and 1 is the bottom border of the target
         * @param startTranslationX value between 0 and 1, where 1 is the width of the target
         * @param endTranslationY value between 0 and 1, where 1 is the width of the target
         * @param startRotationY value between -180 and 180
         * @param endRotationY value between -180 and 180
         * @param startAlpha initial alpha
         * @param endAlpha final alpha
         */
        private FlipEvaluator(float pivotX, float pivotY,
                              float startTranslationX, float endTranslationY,
                              float startRotationY, float endRotationY,
                              float startAlpha, float endAlpha) {
            this.pivotX = pivotX;
            this.pivotY = pivotY;
            this.startTranslationX = startTranslationX;
            this.endTranslationY = endTranslationY;
            this.startRotationY = startRotationY;
            this.endRotationY = endRotationY;
            this.startAlpha = startAlpha;
            this.endAlpha = endAlpha;
        }

        public float getPivotX() {
            return pivotX;
        }

        public float getPivotY() {
            return pivotY;
        }

        public float getTranslationX(float t) {
            return startTranslationX + (endTranslationY - startTranslationX) * t;
        }

        public float getRotationY(float t) {
            return startRotationY + (endRotationY - startRotationY) * t;
        }

        public float getAlpha(float t) {
            return t < .5f ? startAlpha : endAlpha;
        }

    }

}

Your animation files will look like this:

<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
        android:valueFrom="0"
        android:valueTo="1"
        android:propertyName="flipLeftIn"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="1000" />

Of course you can change flipLeftIn with flipLeftOut, flipRightIn or flipRightOut in order to apply the animator to a different property.

In your Activity you can set custom animations in your fragment transaction as usual, specifying the XMLs you previously defined:

    ....
    getFragmentManager()
            .beginTransaction()
            .setCustomAnimations(
                    R.animator.card_flip_right_in, R.animator.card_flip_right_out,
                    R.animator.card_flip_left_in, R.animator.card_flip_left_out)
    ....

Flip example

The other approach is to do everything in the XML, but setting the pivot/translation using a dimension value defined via XML is not as scalable as the solution shown above.

EDIT To reduce the camera distance you can easily use View.setCameraDistance() on API>12. I updated the snippet including this change.

Flip with less skewing

like image 198
a.bertucci Avatar answered Oct 10 '22 13:10

a.bertucci