31

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?

Swifty McSwifterton
  • 2,637
  • 1
  • 30
  • 37

8 Answers8

62

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.

Swifty McSwifterton
  • 2,637
  • 1
  • 30
  • 37
  • @RAS Thanks for the link to the library. :) Maybe next time it could go in the comments rather than editing my answer? I don't know anything about this library so to have it attributed to me is not quite correct. Thanks for your input though! – Swifty McSwifterton Jul 17 '12 at 16:33
  • I understand your point that nobody likes someone editing his/her post without his/her permission. But this answer is not edited by me. It is edited by someone else, I just approved it & solved a grammar mistake done by that person who edited your answer. If you want to check it out, click on the Date/time shown beside "edited", above my name & logo. You can see the full revision history there. – RAS Jul 19 '12 at 06:14
  • 5
    To use this code below 3.0 use the following lib: https://github.com/JakeWharton/NineOldAndroids. Contributed by user: http://stackoverflow.com/users/1117511/loks – Swifty McSwifterton Jul 20 '12 at 01:46
  • Its work but works once for a view, only one time it was flip , second time for the same view its not work – Krunal Shah Feb 11 '14 at 11:40
  • @KrunalShah I ran into the same issue :) This fixed it: http://stackoverflow.com/a/5317467/269876 – Lo-Tan Feb 12 '14 at 21:47
  • 2
    @KrunalShah Actually, just add android:valueFrom="0" to the objectAnimator declaration to fix the problem. – Lo-Tan Feb 12 '14 at 21:55
  • 2
    This results in a rather naive flip, some scaling should be implemented to make it more visually appealing. – A. Steenbergen Mar 28 '15 at 13:08
  • A flip requires two views, one to hide and one to show. How did this answer, which only flips one view, get so many upvotes?? – Justin Jul 03 '18 at 16:02
30

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!

A. Steenbergen
  • 3,360
  • 4
  • 34
  • 51
  • I am trying to translate your algorithm into an XML tween animation. I can't figure out how to set properly the pivotX and pivotY properties: do you have any idea? – Antonio Sesto Mar 27 '15 at 23:23
  • 1
    @AntonioSesto Sorry, I mostly animate without xml because I find animating in code more versatile. I think setting the pivot in xml will be hard, since you don't know the views dimensions. Maybe you can set it as percentage though, have you tried setting the pivot to something like 50%p? – A. Steenbergen Mar 28 '15 at 10:28
  • @A.Steenbergen How can I change the duration? – onCreate Jan 04 '16 at 12:56
  • mFlipAnimator.setDuration(duration) – A. Steenbergen Jan 04 '16 at 15:38
  • 2
    This works flawlessly!! Thank you for posting this answer!! Appreciate the help! +1 from me – Shinta S Aug 03 '16 at 17:52
  • 2
    It took me hours to find an answer that didn't involve fragments, animated both views and not just one, was 3d, and actually worked for the forward and reverse flips. This is it, thanks!! – Justin Jul 03 '18 at 16:29
  • 1
    Really amazing, I made the implementation in kotlin using the rotation in a Relative Layout, it worked perfectly. Thank you – AllanRibas Jun 03 '20 at 03:00
8

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);

    }

}
Tushar Pandey
  • 4,557
  • 4
  • 33
  • 50
5

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();
Ravindra Kushwaha
  • 7,846
  • 14
  • 53
  • 103
5

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.

Ephraim
  • 2,234
  • 1
  • 21
  • 18
  • I realize the question is on how to make the y-axis rotation in the android.com example using xml. But I figured maybe you are interested in this too. – Ephraim Jan 30 '12 at 23:46
3
  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

yoAlex5
  • 29,217
  • 8
  • 193
  • 205
2

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();
Alireza Noorali
  • 3,129
  • 2
  • 33
  • 80
  • I'm revising your answer as poor quality answer, you should add an explanation to your code... – DaFois May 15 '19 at 12:01
0

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;
}
seekingStillness
  • 4,833
  • 5
  • 38
  • 68