Displaying card flip animation on old android

24,048

Solution 1

Found the answer. If you want to do flip animation on ALL ANDROID VERSIONS, use this:

Activity layout file:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_activity_root"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/transparent" >
 
<RelativeLayout
android:id="@+id/main_activity_card_face"
android:layout_width="300dp"
android:layout_height="407dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/front"
android:clickable="true"
android:onClick="onCardClick"
android:padding="5dp" >
</RelativeLayout>
 
<RelativeLayout
android:id="@+id/main_activity_card_back"
android:layout_width="300dp"
android:layout_height="407dp"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="@drawable/back"
android:clickable="true"
android:onClick="onCardClick"
android:visibility="gone" >
</RelativeLayout>
 
</RelativeLayout>

As the layout file flips two view groups you could put anything else inside the view group and it should work. Now lets look at the methods inside the activity which handles calling the flip animation code:

public void onCardClick(View view)
{
      flipCard();
}
 
private void flipCard()
{
    View rootLayout = findViewById(R.id.main_activity_root);
    View cardFace = findViewById(R.id.main_activity_card_face);
    View cardBack = findViewById(R.id.main_activity_card_back);
 
    FlipAnimation flipAnimation = new FlipAnimation(cardFace, cardBack);
 
    if (cardFace.getVisibility() == View.GONE)
    {
        flipAnimation.reverse();
    }
    rootLayout.startAnimation(flipAnimation);
}

And finally the FlipAnimation class:

public class FlipAnimation extends Animation
{
    private Camera camera;
 
    private View fromView;
    private View toView;
 
    private float centerX;
    private float centerY;
 
    private boolean forward = true;
 
    /**
     * Creates a 3D flip animation between two views.
     *
     * @param fromView First view in the transition.
     * @param toView Second view in the transition.
     */
    public FlipAnimation(View fromView, View toView)
    {
        this.fromView = fromView;
        this.toView = toView;
 
        setDuration(700);
        setFillAfter(false);
        setInterpolator(new AccelerateDecelerateInterpolator());
    }
 
    public void reverse()
    {
        forward = false;
        View switchView = toView;
        toView = fromView;
        fromView = switchView;
    }
 
    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight)
    {
        super.initialize(width, height, parentWidth, parentHeight);
        centerX = width/2;
        centerY = height/2;
        camera = new Camera();
    }
 
    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t)
    {
        // Angle around the y-axis of the rotation at the given time
        // calculated both in radians and degrees.
        final double radians = Math.PI * interpolatedTime;
        float degrees = (float) (180.0 * radians / Math.PI);
 
        // Once we reach the midpoint in the animation, we need to hide the
        // source view and show the destination view. We also need to change
        // the angle by 180 degrees so that the destination does not come in
        // flipped around
        if (interpolatedTime >= 0.5f)
        {
            degrees -= 180.f;
            fromView.setVisibility(View.GONE);
            toView.setVisibility(View.VISIBLE);
        }
 
        if (forward)
            degrees = -degrees; //determines direction of rotation when flip begins
 
        final Matrix matrix = t.getMatrix();
        camera.save();
        camera.rotateY(degrees);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }

Here is the link for original post: Displaying card flip animation on old android

UPDATE from @FMMobileFelipeMenezes .

if you want the animation with a smooth scale to flip, change this part of code to (applyTransformation) :

final Matrix matrix = t.getMatrix();
camera.save();
camera.translate(0, 0, Math.abs(degrees)*2);
camera.getMatrix(matrix);
camera.rotateY(degrees);
camera.getMatrix(matrix);
camera.restore();
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX, centerY);

UPDATE from @Hesam There is good tutorial that I recommend to read it. Although it's not as nice as Android tutorial based on fragments, it's worth to be read and useful if you want to assign animation to layouts and views as well as have it on old APIs.

Use Android's scale animation to simulate a 3D flip

Improved project on github by @LenaBru

Solution 2

I used Flextra code below, and if you want the animation with a smooth scale to flip, change this part of code to (applyTransformation) :

    final Matrix matrix = t.getMatrix();
    camera.save();
    camera.translate(0, 0, Math.abs(degrees)*2);
    camera.getMatrix(matrix);
    camera.rotateY(degrees);
    camera.getMatrix(matrix);
    camera.restore();
    matrix.preTranslate(-centerX, -centerY);
    matrix.postTranslate(centerX, centerY);

Solution 3

I played with this all day, and finally achieved the ultimate goal - a smooth cardflip like rotation animation of two views!

I put demo project here

public class FlipAnimation extends Animation {
    private Camera camera;

    private View fromView;
    private View toView;

    private float centerX;
    private float centerY;

    private boolean forward = true;


    /**
     * Creates a 3D flip animation between two views.
     * 
     * @param fromView
     *            First view in the transition.
     * @param toView
     *            Second view in the transition.
     */
    public FlipAnimation(View fromView, View toView) {
        this.fromView = fromView;
        this.toView = toView;

        setDuration(1500);
        setFillAfter(false);
        // setInterpolator(new AccelerateDecelerateInterpolator());
        setInterpolator(new LinearInterpolator());
    }

    public void reverse() {

        if (forward) {
            View switchView = toView;
            toView = fromView;
            fromView = switchView;
        }
        forward = false;
    }

    @Override
    public void initialize(int width, int height, int parentWidth, int parentHeight) {
        super.initialize(width, height, parentWidth, parentHeight);
        centerX = width / 2;
        centerY = height / 2;
        camera = new Camera();
    }


    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        // Angle around the y-axis of the rotation at the given time
        // calculated both in radians and degrees.
        final double radians = Math.PI * interpolatedTime;
        float degrees = (float) (180.0 * radians / Math.PI);


        //scale down the views a bit, so that they would look nice when the rotation begins

        if (interpolatedTime <= 0.05f) {
            fromView.setScaleX(1 - interpolatedTime);
            fromView.setScaleY(1 - interpolatedTime);
            toView.setScaleX(1 - interpolatedTime);
            toView.setScaleY(1 - interpolatedTime);
        }

        // Once we reach the midpoint in the animation, we need to hide the
        // source view and show the destination view. We also need to change
        // the angle by 180 degrees so that the destination does not come in
        //It is very important to call "toView.bringToFront()" and not play with the
        // visibility of the views, because if you apply this animation more than once,
        //the subsequent calls may fail
        if (interpolatedTime >= 0.5f) {
            degrees -= 180.f;
            toView.bringToFront();
          //these two lines force a layout redraw
          ((View)toView.getParent()).requestLayout();
          ((View)toView.getParent()).invalidate();


        }

        //scale the views back to their original size (Assuming original size was 1)
        if (interpolatedTime >= 0.95f) {
            fromView.setScaleX(interpolatedTime);
            fromView.setScaleY(interpolatedTime);
            toView.setScaleX(interpolatedTime);
            toView.setScaleY(interpolatedTime);
        }

        if (forward)
            degrees = -degrees; // determines direction of rotation when flip
                                // begins

        final Matrix matrix = t.getMatrix();
        camera.save();
        camera.translate(0, 0, Math.abs(degrees) * 2);
        camera.getMatrix(matrix);
        camera.rotateY(degrees);
        camera.getMatrix(matrix);
        camera.restore();
        matrix.preTranslate(-centerX, -centerY);
        matrix.postTranslate(centerX, centerY);
    }
}

and call it like this

import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Toast;

public class MainActivity extends FragmentActivity {

private boolean showingBack;
private FragmentLeft left = new FragmentLeft();
private FragmentRight right = new FragmentRight();
private Context context;
private Handler handler;
private FlipAnimation flipAnimation;
private FlipAnimation backFlip;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    context = this;
    handler = new Handler(getMainLooper());

    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, right, "fragmentRight").commit();
    getSupportFragmentManager().beginTransaction().add(R.id.fragment_container, left, "fragmentLeft").commit();
    findViewById(R.id.flip).setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            flipAnimation = new FlipAnimation(left.getView(), right.getView());
            backFlip = new FlipAnimation(left.getView(), right.getView());
            handler.removeCallbacks(rotate);
            handler.postDelayed(rotate, 100);
        }

    });
}

    private Runnable rotate = new Runnable() {

        @Override
        public void run() {
           //put a variable showingBack, do not rely on view properties to flip
            if (!showingBack) {
                //very important to flip both views, so that when the
                //left view goes to back and right view goes to front,
                //the right view finishes the rotation
                left.getView().startAnimation(flipAnimation);
                right.getView().startAnimation(flipAnimation);
                Toast.makeText(context, "flip", Toast.LENGTH_LONG).show();
                showingBack = true;
            } else {
                showingBack = false;
                backFlip.reverse();
                Toast.makeText(context, "backflip", Toast.LENGTH_LONG).show();
                //very important to flip both views, so that when the
                //right view goes to back and right view goes to front,
                //the left view finishes the rotation
                left.getView().startAnimation(backFlip);
                right.getView().startAnimation(backFlip);

            }
        }
    };

}

These are the fragments

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentRight extends Fragment {


    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_right, container,false);
    }
}

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FragmentLeft extends Fragment {


    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_left, container,false);
    }
}

and finally the view itself

activity_main.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:background="#ff151515"
    tools:context="com.example.flipviewtest.MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/hello_world" />

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true" >
    </FrameLayout>

    <Button
        android:id="@+id/flip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:text="flip" />

</RelativeLayout>

fragment_left.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffff0000"
     >

    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#ff0ffff0"
        android:layout_margin="20dp" />

</LinearLayout>

fragment_right.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#ff00ff00"
    android:orientation="vertical" >

    <View
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        android:background="#ff0000ff" />

</LinearLayout>

note some of the code taken from Flextra's and @FMMobileFelipeMenezes answers

Solution 4

There is good tutorial that I recommend to read it. Although it's not as nice as Android tutorial based on fragments, it's worth to be read and useful if you want to assign animation to layouts and views as well as have it on old APIs.

Use Android's scale animation to simulate a 3D flip

Share:
24,048

Related videos on Youtube

Alexander.Iljushkin
Author by

Alexander.Iljushkin

Hello, friend. I'm pleased to meet you.

Updated on July 18, 2022

Comments

  • Alexander.Iljushkin
    Alexander.Iljushkin almost 2 years

    We all know this article of how to create "card filp" animations using new api. But how can I make this on apis < 3.0?

    Update:

    As long as there are good and easy-to-use libraries like android-FlipView I don't think you really need to do such animation by yourself.

  • Cilenco
    Cilenco about 11 years
    Great! Tank you for this awesome code. Only one question: How can I rotate to a Fragment which is not int the current activity? I can get the View from the Fragment with Fragment.getView() but then it comes to an error if I start the animation.
  • Alexander.Iljushkin
    Alexander.Iljushkin about 11 years
    Cilenco, try to redefine fragment class and put this code inside. Or create an action listener. Anyway I'm not pretty sure if that help. Have not deal with fragments yet.
  • Manann Sseth
    Manann Sseth about 11 years
    Thanks Dude..+1.. Even It's working inside Fragment too.. Keep It up.:)
  • Nilesh Verma
    Nilesh Verma almost 11 years
    i want to flip the 2 images in single view i.e in single imageview please help?
  • Alexander.Iljushkin
    Alexander.Iljushkin almost 11 years
    @NileshVerma why not. just add two image views together inside of the android:id="@+id/main_activity_card_face" or android:id="@+id/main_activity_card_back"
  • Nilesh Verma
    Nilesh Verma almost 11 years
    @A.P no i can not bcoz i m doing some other stuff using that single imageview please help flip within two images in single imageview
  • Alexander.Iljushkin
    Alexander.Iljushkin almost 11 years
    @NileshVerma please provide more detailed information about how and what you're trying to make. For now, i don't inderstand what exactly you want.
  • Bhavik Mehta
    Bhavik Mehta over 9 years
    Hi, can any one please tell me how to use it with android ListView's list item with front and back views? in CustomAdapter
  • Alexander.Iljushkin
    Alexander.Iljushkin over 9 years
    @BhavikMehta Flip Animation now awailable when using official Android Studio. You can download android studio free from google android pages. When you create a new project, you'll be asked about desired type of example project. Choose Flip type. Choose desired min API, f.i. 2, if you choose 2x api level, a new project will be generated using support library and alot of useful code that you should write by yourself. To make it work, just do your custom listview class and apply similar code to it. you should think about how to apply similar activity animation
  • Alexander.Iljushkin
    Alexander.Iljushkin over 9 years
    @BhavikMehta also look for something like that stackoverflow.com/questions/27626750/…
  • Darshn
    Darshn over 8 years
    You saved my life! Thanks a lot! you live 100 years! may be more than that :D
  • Alexander.Iljushkin
    Alexander.Iljushkin over 8 years
    @Darsh, in the new Android Studio there is now new wizard where you can choose complete flipping activity out of the box. Just download Android Studio from google.
  • David
    David almost 8 years
    is that possible to change the background color(or image) during the animation?
  • Eli
    Eli almost 7 years
    Nice! somehow for me flip id bad on top and bottom of the screen, angles and center are not aligned during flip, any ideas?
  • Alexander.Iljushkin
    Alexander.Iljushkin almost 7 years
    @Elisa, download new android studio, there's integrated flip view layout from new project creation wizard (as I remember). Also look at the question's description, there is a link to the flipview library
  • Eli
    Eli almost 7 years
    @Alexander.Iljushkin Thanks! I hadn't noticed the ViewFlipper layout.
  • Trevor Wood
    Trevor Wood over 6 years
    This is amazing. Works so much better than even googles tutorial. They should just link their tutorial to this answer.