Swifty McSwifterton
Swifty McSwifterton

Reputation: 2667

Creating a 3D flip animation in Android using XML

I have created a 3D flip of a view using this android tutorial However, I have done it programmatically and I would like to do it all in xml, if possible. I am not talking about simply shrinking a view to the middle and then back out, but an actual 3D flip.

Is this possible via xml?

Upvotes: 32

Views: 59304

Answers (8)

A. Steenbergen
A. Steenbergen

Reputation: 3440

Since the answers to this question are fairly dated, here is a more modern solution relying on ValueAnimators. This solution implements a true, visually appealing 3D-flip, because it not just flips the view, but also scales it while it is flipping (this is how Apple does it).

First we set up the ValueAnimator:

mFlipAnimator = ValueAnimator.ofFloat(0f, 1f);
mFlipAnimator.addUpdateListener(new FlipListener(frontView, backView));

And the corresponding update listener:

public class FlipListener implements ValueAnimator.AnimatorUpdateListener {

    private final View mFrontView;
    private final View mBackView;
    private boolean mFlipped;

    public FlipListener(final View front, final View back) {
        this.mFrontView = front;
        this.mBackView = back;
        this.mBackView.setVisibility(View.GONE);
    }

    @Override
    public void onAnimationUpdate(final ValueAnimator animation) {
        final float value = animation.getAnimatedFraction();
        final float scaleValue = 0.625f + (1.5f * (value - 0.5f) * (value - 0.5f));

        if(value <= 0.5f){
            this.mFrontView.setRotationY(180 * value);
            this.mFrontView.setScaleX(scaleValue);
            this.mFrontView.setScaleY(scaleValue);
            if(mFlipped){
                setStateFlipped(false);
            }
        } else {
            this.mBackView.setRotationY(-180 * (1f- value));
            this.mBackView.setScaleX(scaleValue);
            this.mBackView.setScaleY(scaleValue);
            if(!mFlipped){
                setStateFlipped(true);
            }
        }
    }

    private void setStateFlipped(boolean flipped) {
        mFlipped = flipped;
        this.mFrontView.setVisibility(flipped ? View.GONE : View.VISIBLE);
        this.mBackView.setVisibility(flipped ? View.VISIBLE : View.GONE);
    }
}

That's it!

After this setup you can flip the views by calling

mFlipAnimator.start();

and reverse the flip by calling

mFlipAnimator.reverse();

If you want to check if the view is flipped, implement and call this function:

private boolean isFlipped() {
    return mFlipAnimator.getAnimatedFraction() == 1;
}

You can also check if the view is currently flipping by implementing this method:

private boolean isFlipping() {
    final float currentValue = mFlipAnimator.getAnimatedFraction();
    return (currentValue < 1 && currentValue > 0);
}

You can combine the above functions to implement a nice function to toggle the flip, depending on if it is flipped or not:

private void toggleFlip() {
    if(isFlipped()){
        mFlipAnimator.reverse();
    } else {
        mFlipAnimator.start();
    }
}

That's it! Simple and easy. Enjoy!

Upvotes: 30

Alireza Noorali
Alireza Noorali

Reputation: 3265

Just put the view which you're going to animate it in place of viewToFlip.

ObjectAnimator flip = ObjectAnimator.ofFloat(viewToFlip, "rotationY", 0f, 360f); // or rotationX
flip.setDuration(2000); // 2 seconds
flip.start();

Upvotes: 2

yoAlex5
yoAlex5

Reputation: 34175

  1. The simplest way to do it is using ViewPropertyAnimator

    mImageView.animate().rotationY(360f);
    

    Using the fluent interface you can build more complex and exciting animation. E.g. you can enable hardware acceleration just call withLayer() method(API 16). More here

  2. If you want to figure out how to create 3d flick animation, please follow here and here

  3. I implemended my own solution only for a research. It includes: cancelation, accelleration, support API >= 15 and is based on Property Animation. The entire animation includes 4 parts, 2 for each side. Every objectAnimator has a listener that defines current animation index and represents an image in the onAnimationStart and current play time value in the onAnimationCancel. It looks like

    mQuarterAnim1.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationStart(Animator animation) {
            mQuarterCurrentAnimStartIndex = QUARTER_ANIM_INDEX_1;
            mImageView.setImageResource(mResIdFrontCard);
        }
    
        @Override
        public void onAnimationCancel(Animator animation) {
            mQuarterCurrentAnimPlayTime = ((ObjectAnimator) animation).getCurrentPlayTime();
        }
    });
    

    For start set call

    mAnimatorSet.play(mQuarterAnim1).before(mQuarterAnim2)
    

    If AnimatorSet was canceled we can calculate delta and run the reverse animation relying on the current index animation and the current play time value.

    long degreeDelta = mQuarterCurrentAnimPlayTime * QUARTER_ROTATE / QUARTER_ANIM_DURATION;
    
    if (mQuarterCurrentAnimStartIndex == QUARTER_ANIM_INDEX_1) {
        mQuarterAnim4.setFloatValues(degreeDelta, QUARTER_FROM_1);
        mQuarterAnim4.setDuration(mQuarterCurrentAnimPlayTime);
    
        mAnimatorSet.play(mQuarterAnim4);
    }
    

A full code snippet you can find here

Upvotes: 3

seekingStillness
seekingStillness

Reputation: 5093

Adding to A. Steenbergen's great answer. When flipping the same view (updating a TextView for example) I removed the View.Visibility change in the constructor in order to keep the transition smoother.

public FlipListener(final View front, final View back) {
    this.mFrontView = front;
    this.mBackView = back;
}

Upvotes: 0

Ravindra Kushwaha
Ravindra Kushwaha

Reputation: 8032

One of the better solution to flip the image with out use of the resource animation , is as follow:-

  ObjectAnimator animation = ObjectAnimator.ofFloat(YOUR_IMAGEVIEW, "rotationY", 0.0f, 360f);  // HERE 360 IS THE ANGLE OF ROTATE, YOU CAN USE 90, 180 IN PLACE OF IT,  ACCORDING TO YOURS REQUIREMENT 

  animation.setDuration(500); // HERE 500 IS THE DURATION OF THE ANIMATION, YOU CAN INCREASE OR DECREASE ACCORDING TO YOURS REQUIREMENT
  animation.setInterpolator(new AccelerateDecelerateInterpolator());
  animation.start();

Upvotes: 5

Tushar Pandey
Tushar Pandey

Reputation: 4857

I have created a simple program for creating flip of view like :

enter image description here

In Activity you have to create this method, for adding flip_rotation in view.

private void applyRotation(View view) 
{
    final Flip3dAnimation rotation = new Flip3dAnimation(view);
    rotation.applyPropertiesInRotation();
    view.startAnimation(rotation);
}

for this, you have to copy main class used to provide flip_rotation.

import android.graphics.Camera;
import android.graphics.Matrix;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Transformation;

public class Flip3dAnimation extends Animation {
    private final float mFromDegrees;
    private final float mToDegrees;
    private final float mCenterX;
    private final float mCenterY;
    private Camera mCamera;

    public Flip3dAnimation(View view) {
        mFromDegrees = 0;
        mToDegrees = 720;
        mCenterX = view.getWidth() / 2.0f;
        mCenterY = view.getHeight() / 2.0f;
    }

    @Override
    public void initialize(int width, int height, int parentWidth,
            int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        mCamera = new Camera();
    }

    public void applyPropertiesInRotation()
    {
        this.setDuration(2000);
        this.setFillAfter(true);
        this.setInterpolator(new AccelerateInterpolator());
    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        final float fromDegrees = mFromDegrees;
        float degrees = fromDegrees
                + ((mToDegrees - fromDegrees) * interpolatedTime);

        final float centerX = mCenterX;
        final float centerY = mCenterY;
        final Camera camera = mCamera;

        final Matrix matrix = t.getMatrix();

        camera.save();

        Log.e("Degree",""+degrees) ;
        Log.e("centerX",""+centerX) ;
        Log.e("centerY",""+centerY) ;

        camera.rotateY(degrees);

        camera.getMatrix(matrix);
        camera.restore();

        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);

    }

}

Upvotes: 8

Swifty McSwifterton
Swifty McSwifterton

Reputation: 2667

Here is the answer, though it only works with 3.0 and above.

1) Create a new resources folder called "animator".

2) Create a new .xml file which I will call "flipping". Use the following xml code:

<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
    android:valueFrom="0" android:valueTo="360" android:propertyName="rotationY" >
</objectAnimator>

No, the objectAnimator tags do not start with an uppercase "O".

3) Start the animation with the following code:

ObjectAnimator anim = (ObjectAnimator) AnimatorInflater.loadAnimator(mContext, R.animator.flipping); 
anim.setTarget(A View Object reference goes here i.e. ImageView);
anim.setDuration(3000);
anim.start();

I got all this from here.

Upvotes: 62

Ephraim
Ephraim

Reputation: 2244

The tutorial or the link by om252345 don't produce believable 3D flips. A simple rotation on the y-axis isn't what's done in iOS. The zoom effect is also needed to create that nice flip feel. For that, take a look at this example. There is also a video here.

Upvotes: 5

Related Questions