Circular Reveal Effect like WhatsApp in Android

While sending a photo through WhatsApp, I noticed the ‘Attach’ button performs a neat Circular Reveal effect. My phone being JellyBean, I was surprised to see a Lollipop only transition. Thanks to a neat library, we can mimic the exact same thing for pre Lollipop too.

The Reveal Effect

Touch feedback in material design provides an instantaneous visual confirmation at the point of contact when users interact with UI elements.

The Reveal Effect uses the RippleDrawable class to achieve this. So visually, you can tell that it is similar to the Ripple effect. The only difference being that a Ripple is a touch feedback denoted by a color spreading from a point of contact, while the Reveal effect does the same animation, but for displaying a view.

WhatsApp’s Version

Here’s how it looks on WhatsApp, after their Material Design upgrade.

whatsapp-reveal-effect

This is exactly what we’re going to create. If you still don’t get what I’m talking about then scroll down to the end of the post.

Setup

The issue is, this animation as said before is intended for Lollipop and above ONLY. There is no support library to help us out here. So no version below Lollipop can perform this.

However, there is a neat library that’s managed to backport this for us. We will be using the same, for our purpose. The good thing about the library is that, the methods are exactly the same, which is convenient. Though one must be careful about the imports.

Circular Reveal Library

We’ll start by including the library in our build.gradle file. Add the repository first, and then the dependency.

repositories {
    maven {
        url "https://jitpack.io"
    }
}

dependencies {
    compile 'com.github.ozodrukh:CircularReveal:1.1.0'
}

Layout

Let’s create a layout similar to that of WhatsApp’s Attach option.

layout-2015-07-17-092633

Here’s the layout skeleton if you’re interested. Honestly this layout could be any layout that you want to reveal using this effect, even entire screen transitions could be done.

<io.codetail.widget.RevealFrameLayout
           android:layout_width="match_parent"
           android:layout_height="wrap_content">

           <LinearLayout
               android:id="@+id/reveal_items"
               android:layout_width="match_parent"
               android:layout_height="wrap_content"
               android:orientation="horizontal"  >

               <LinearLayout
                   android:layout_width="0dp"
                   android:layout_height="wrap_content"
                   android:layout_weight="1"
                   android:gravity="center"
                   android:orientation="vertical">

                   <ImageButton
                       android:layout_width="70dp"
                       android:layout_height="70dp"
                       android:background="@drawable/icon_camera" />

                   <TextView
                       android:layout_width="wrap_content"
                       android:layout_height="wrap_content"
                       android:layout_marginTop="16dp"
                       android:text="Gallery" />
               </LinearLayout>

               <!-- Other 2 icons here-->
               
           </LinearLayout>

       </io.codetail.widget.RevealFrameLayout>

Note the RevealFrameLayout widget. This comes from the library we imported. This is nothing but a FrameLayout. To perform the animation, we will reference the immediate child layout of this frame. While that child (here being the LinearLayout) with id reveal_items will animate, it’s shape will be clipped by the outer RevealFrameLayout.

Performing the Reveal Effect

Head over to your Activity.java and lets get that animation working. The animation will be toggled when we tap the Attach (clip) button. The contents of the RevealFrameLayout will be displayed, and hidden alternatively.

Remember I said we need to reference the immediate child of the RevealFrameLayout.

LinearLayout mRevealView = (LinearLayout) findViewById(R.id.reveal_items);
mRevealView.setVisibility(View.INVISIBLE);

The view will be inivisible as by default it remains hidden, only presenting itself when we tap Attach.

Our Attach button will be in our Toolbar, included using a menu resource.

... 
<item
        android:id="@+id/action_clip"
        android:icon="@drawable/ic_action_editor_attach_file"
        android:title="Media"
        app:showAsAction="ifRoom" />
...

The meat of the action takes place in the onOptionsItemSelected() method. Here is where the Toolbar (ActionBar) menu item’s click actions are defined.

 public boolean onOptionsItemSelected(MenuItem item) {
  
   switch (item.getItemId()) {
         case R.id.action_clip:
        // handle animation here
             return true;  
     }

     return super.onOptionsItemSelected(item);
}

Animation Prerequisites

To perform the animation, two parameters are needed:

  1. X, Y coordinates to start the animation from
  2. Radius of the circular reveal

WhatsApp performs the Reveal effect from the top right.

int cx = (mRevealView.getLeft() + mRevealView.getRight());
int cy = mRevealView.getTop();

If you need it from the center, then just change cy to this instead:  int cy = (mRevealView.getTop() + mRevealView.getBottom())/2;

The radius can be obtained using this method:

int radius = Math.max(mRevealView.getWidth(), mRevealView.getHeight());

ViewAnimationUtils

Now we can finally initiate the reveal effect.

SupportAnimator animator =
                      ViewAnimationUtils.createCircularReveal(mRevealView, cx, cy, 0, radius);
              animator.setInterpolator(new AccelerateDecelerateInterpolator());
              animator.setDuration(400);

SupportAnimator animator_reverse = animator.reverse();

NOTE: Be very careful of the imports here. ViewAnimationUtils is imported from from the external library, and NOT the default android.view.ViewAnimationUtils import.

animator will be used for revealing the view on tap, while animator_reverse in essense reverses the animation for hiding back the view when tapped again.

Animation Toggle

We will use a simple boolean hidden  to handle the toggling.

if (hidden) {
                  mRevealView.setVisibility(View.VISIBLE);
                  animator.start();
                  hidden = false;

              } else {

                  animator_reverse.addListener(new SupportAnimator.AnimatorListener() {
                  
                      @Override
                      public void onAnimationEnd() {
                          mRevealView.setVisibility(View.INVISIBLE);
                          hidden = true;
                      }
                      ...
                  });

                  animator_reverse.start();
              }

Note that we need to make the view visible before we being the reveal effect. While this simply works, its not the same for the reverse effect. The library’s SupportAnimator provides a method reverse() to reverse the animation. We cannot just set the view invisible before starting the animation. The animation performed will never be visible.

For this we will use the AnimatorListener. Once the reverse animation ends, the convenient onAnimationEnd() method will allow us to hide the view.

Be sure to update the boolean hidden accordingly in the if and else cases.


Lollipop Fix

Strangely, this doesn’t seem to work on Lollipop 5.0.1 and above. As in, the transition itself doesn’t work. A simple workaround for this would be to use Android’s native ViewAnimationUtils for devices Lollipop and above.

Simply wrap the code you just wrote like this.

 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
    // Your previously written code
} else {
  // Same code here but import ViewAnimationUtils from android.view.ViewAnimationUtils
   Animator anim = android.view.ViewAnimationUtils.createCircularReveal(mRevealView, cx, cy, 0, radius);
  ...
}

Hope the comments there were self explanatory. There’s no need for the support animator as well. You can look at my code here, just in case.


That’s it. While this post, for the most part was just my explaining, the actual implementation itself is pretty simply and straightforward once you do it yourself. The Circular Reveal library by ozodrukh is brilliant and works nicely.

Here’s the final output and it looks amazing.

reveal-effect-final

As always, here’s the link to my Git for code reference.

You can get creative with which views your using, be it a simple view or an entire screen transition itself! I simply used WhatsApp’s Attach button as an example.

Show me what you can come up with and drop em in the comments below.

Suleiman

Product Designer who occasionally writes code.

You may also like...

23 Responses

  1. Biswajit Jena says:

    Hii thanks for this awesome post. Little help. How can I close the view when clicking outside the revealed layout.?

  2. Olayinka says:

    You know what would be nice? XML.

  3. Rehan.CH. says:

    please share the link of complete source code

  4. Nayanesh Dalvi says:

    how can i make below layout nonclickable and on outside click it shoud close same as whatsapp

  5. Tariq Mahmud says:

    How to get the revealed layout on the top of my other views? Just like whatsapp, here my revealed layout will push the other views down and make place for its self. But i want the layout to reveal over the top of current view. Needs your help here plzz..

  6. Suhaib Khan says:

    Thanks a ton! This is exactly what I was looking for 🙂 Reveal animation is buttery smooth but when i integrated Google AdMob banner ads to my app, reveal animation became buggy. I had read somewhere that AdMob causes FPS drops in android games, but i have just used reveal animation to a small fragment in my app. Is there a way to overcome this problem. I mean, using Reveal animation and AdMob together without any lags in the animation.

    • Suleiman19 says:

      Frankly speaking, I haven’t tried using a reveal effect on an ad unit. If the performance takes a significant hit, try to make do without. Your ad could simply be placed at the bottom or attached in a list. Why need a fancy animation for it?

      • Suhaib Khan says:

        I haven’t applied reveal effect to the ad unit. The ad unit is not even placed in the activity where this reveal effect is integrated. See, I have two activities in my app (activity A and activity B). Reveal effect is applied to a fragment in activity B and the Google AdMob ad is placed at the bottom in activity A and still animation becomes buggy? Can you find a fix for this? This reveal effect would be of no use if we can’t integrate Ads in our apps 🙁

      • Suleiman19 says:

        Sorry Suhaib, I’m afraid I can’t be of help here since I didn’t make the library. However I suggest you post this as an issue in GitHub. Hopefully that library’s developer could solve the issue for you?

  7. Subhrajyoti Sen says:

    Another amazing post Suleiman. I was looking for this exact thing. But I’m not able to understand to use it has a transition. Since i would be starting from a FAB in first activity and then revealing the LinearLayout of next activity, I cant understand how to connect them

    • Suleiman19 says:

      Glad you liked the post Subhrajyoti.
      The trick is not to launch a new activity using the transition, but to include BOTH layouts in the same activity. You can toggle each using the FAB.

      // your 1st screen layout containing FAB

      // 2nd screen. Toggle this.

      • Subhrajyoti Sen says:

        You are a life saver. Thank you so much for replying. I definitely have to add you to my app’s credit list

      • Suleiman19 says:

        Happy that it helped you. Haha, you’re too kind. By the way, what’s your app about? Link on play store if any?

  8. Amazing Post !! Would implement this soon !!

Leave a Reply

Your email address will not be published. Required fields are marked *