Layout Flip Animation in Android

    Sometimes, we would like to build a 2-sides layout with 2 separate contents like playing cards and we can flip/switch them smoothly with animation.

    By this tutorial post, I will show you how to do a card flip animation with custom fragment animations. Card flips animate between views of content by showing an animation that emulates a card layout flipping over.
    And our output (flip animation) will be like this:

Create the animators

    Create the animations for the layout flips. You'll need two animators for when the front of the card animates out and to the left and in and from the left. You'll also need two animators for when the back of the card animates in and from the right and out and to the right. Now, create animator folder under res and add these animation files (XML):
card_flip_left_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Before rotating, immediately set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:duration="0" />

    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="-180"
        android:valueTo="0"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
    <objectAnimator
        android:valueFrom="0.0"
        android:valueTo="1.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>
card_flip_left_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="0"
        android:valueTo="180"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>
card_flip_right_in.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Before rotating, immediately set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:duration="0" />

    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="180"
        android:valueTo="0"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 1. -->
    <objectAnimator
        android:valueFrom="0.0"
        android:valueTo="1.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>
card_flip_right_out.xml
<set xmlns:android="http://schemas.android.com/apk/res/android">
    <!-- Rotate. -->
    <objectAnimator
        android:valueFrom="0"
        android:valueTo="-180"
        android:propertyName="rotationY"
        android:interpolator="@android:interpolator/accelerate_decelerate"
        android:duration="@integer/card_flip_time_full" />

    <!-- Half-way through the rotation (see startOffset), set the alpha to 0. -->
    <objectAnimator
        android:valueFrom="1.0"
        android:valueTo="0.0"
        android:propertyName="alpha"
        android:startOffset="@integer/card_flip_time_half"
        android:duration="1" />
</set>

Create 2 Fragment views

    Each side of the "card" layout is a Fragment which has a separate layout that can contain any content you want. You'll then use the two layouts in the Fragments that you'll later animate. The following layout is "front side" which shows an image:
fragment_card_front.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:contentDescription="@null"
        android:scaleType="fitXY"
        android:src="@drawable/android_team" />
</LinearLayout>
    And this is "back layout" which contains some texts:
fragment_card_back.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#a6c"
    android:orientation="vertical"
    android:padding="@dimen/activity_horizontal_margin">

    <TextView
        android:id="@android:id/text1"
        style="?android:textAppearanceLarge"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/card_back_title"
        android:textColor="#fff"
        android:textStyle="bold" />

    <TextView
        style="?android:textAppearanceSmall"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:lineSpacingMultiplier="1.2"
        android:text="@string/card_back_description"
        android:textAllCaps="true"
        android:textColor="#80ffffff"
        android:textStyle="bold" />

</LinearLayout>

Fragments programmatically code

    Create Fragments classes for the front and back of the "card" layout. These classes return the layouts that you created previously in the onCreateView() method of each Fragment. You can then create instances of this Fragment in the parent Activity where you want to show the layout:
FrontLayoutFragment.java
package info.devexchanges.animationfliplayout;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class FrontLayoutFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_card_front, container, false);
    }
}
BackLayoutFragment.java
package info.devexchanges.animationfliplayout;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class BackLayoutFragment extends Fragment{

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

Animate "Card" layout in the Activity

    Now, you'll need to display the Fragments inside of an Activity. To do this, first create the layout for your Activity. In my example, I create a RelativeLayout that I can add Fragments to later, I also put a Button inside it to flip this layout with animation when clicked:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/btn_flip"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        android:text="Flip Layout" />
</RelativeLayout>
    In the programmatically code, set the content view to be the layout that you just created. It's also good idea to show a default fragment when the activity is created, I will display the front of the "card" layout by default:
public class MainActivity extends AppCompatActivity implements FragmentManager.OnBackStackChangedListener {

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

        if (savedInstanceState == null) {
            getFragmentManager()
                    .beginTransaction()
                    .add(R.id.container, new FrontLayoutFragment())
                    .commit();
        } else {
            isShowingBackLayout = (getFragmentManager().getBackStackEntryCount() > 0);
        }

        getFragmentManager().addOnBackStackChangedListener(this);

        View btnFlip = findViewById(R.id.btn_flip);
        btnFlip.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                flipCard();
            }
        });
    }
}
    Now that you have the front of the card showing, you can show the back of the "card" layout with the flip animation at an appropriate time. Create a method to show the other side of the card that does the following things (this method named flipCard() and will be invoked when the Button clicked):
  • Sets the custom animations that you created earlier for the fragment transitions. 
  • Replaces the currently displayed fragment with a new fragment and animates this event with the custom animations that you created. 
  • Adds the previously displayed fragment to the fragment back stack so when the user presses the Back button, the card flips back over.
This is the remaining code of MainActivity:
        private boolean isShowingBackLayout = false;

        private void flipCard() {
        if (isShowingBackLayout) {
            getFragmentManager().popBackStack();
            return;
        }
        isShowingBackLayout = true;

        getFragmentManager().beginTransaction()
                // Replace the default fragment animations with animator resources representing
                // rotations when switching to the back of the card, as well as animator
                // resources representing rotations when flipping back to the front (e.g. when
                // the system Back button is pressed).
                .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)

                // Replace any fragments currently in the container view with a fragment
                // representing the next page (indicated by the just-incremented currentPage
                // variable).
                .replace(R.id.container, new BackLayoutFragment())

                // Add this transaction to the back stack, allowing users to press Back
                // to get to the front of the card.
                .addToBackStack(null)
                .commit();
    }

    @Override
    public void onBackStackChanged() {
        isShowingBackLayout = (getFragmentManager().getBackStackEntryCount() > 0);
    }
    As you can see at the DEMO output above, when the back layout is showing, if user press Back button, the front layout will be inflated. Conversely, app will be closed!

Conclusions

    By adding animation to fragments transaction process, we now able to make a card flip animation easily. Hope this trick is helpful with your own work. Further, you can visit this tag link to read all posts about creating animation in Android. Finally, you can download full source code by click the button below!


    References to original post on Android Developer blog.

Share


Previous post
Next post
Next Post »