Over a million developers have joined DZone.

How We Developed the Guillotine Menu Animation for Android

DZone's Guide to

How We Developed the Guillotine Menu Animation for Android

Learn about how to implement a "guillotine menu" in your Android apps.

· Mobile Zone
Free Resource

You must’ve read our story about how our designer Vitaly Rubtsov and iOS developer Maksym Lazebnyi created an unconventional top bar animation which received an ominous name – Guillotine Menu (you can see the iOS animation on Dribbble and GitHub). Soon after, our Android developer Dmytro Denysenko took up the challenge to implement the same animation but on the Android platform (check it out on GitHub). He couldn’t even predict what difficulties he'd have to face and how deep he'd have to dive in search of the solution.

Where to Start?

At first, I wanted to resort to standard solutions to implement the component for Android. After all, it seemed possible at first sight. I had a plan to use ObjectAnimation to implement rotation of the navigation view. I also wanted to add a default BounceInterpolator to achieve a rebound effect when the menu collides with the left border of the screen. However, the BounceInterpolator appeared to make the rebound too powerful, as if it’s a rebound of a soccer ball, and hardly the one of a metal guillotine.

The default BounceInterpolator doesn’t provide any parameters for customization, so I had no other choice but to write my own interpolator. It was supposed to not only add a rebound, but also create a free fall acceleration effect to make the animation look more natural.

The Guillotine component in our animation consists of Guillotine's rotation, Guillotine's rebound, and action bar's rebound. What’s more, I used two custom interpolators to implement the effects of free fall acceleration and rebound. Now it's time to walk you through the development process.

How We Implemented Rotation of the Guillotine Menu

I needed to do two things to rotate the animation: find the centre of rotation, and implement ObjectAnimation to do the actual rotation.

Before I could calculate the center of rotation I had to put the layout on the screen.

private void setUpOpeningView(final View openingView) {

   if (mActionBarView != null) {

       mActionBarView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {


           public void onGlobalLayout() {

               if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {


               } else {









private float calculatePivotY(View burger) {

   return burger.getTop() + burger.getHeight() / 2


private float calculatePivotY(View burger) {

   return burger.getTop() + burger.getHeight() / 2;


After that, I only had to add a few lines of code:

ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(mGuillotineView, "rotation", GUILLOTINE_OPENED_ANGLE, GUILLOTINE_CLOSED_ANGLE);

/* setting duration, listeners, interpolator, etc. */


The centre of rotation is actually the centre of a hamburger. The animation requires two hamburgers: one on the main action bar, and the other on the Guillotine layout. To make the animation look smooth both hamburgers had to be identical and use the same coordinates. To achieve this, I just created a hamburger on a toolbar (which you can’t see) and evened it with the center to the Guillotine menu hamburger. 

How We Implemented Free Fall and Rebound

To implement the Guillotine menu animation for iOS, my colleague Maksym Lazebnyi used a default UIDynamicItemBehavior class which was customized with the help of elasticity and resistance properties. However, it isn’t that easy on Android.

[Standard Android interpolator]

As I mentioned earlier, I could use a default BounceInterpolator for the layout rotation but it appeared to give a too soft rebound (as if our guillotine were a ball). That’s why I tried to implement a custom interpolator. It was supposed to add acceleration to the animation.

The interpolation rate varies from 0 to 1 ratio. In my case, the angle of rotation goes from 0° to 90° (clockwise). This means that at 0° angle the interpolation rate would be "0" as well (initial position), and when the angle equals 90°, the interpolation ratio would be "1" (final position).

Since our interpolation has a quadratic dependence, it allows for both rebound and free fall just like on Vitaly’s animated screenshot. I had to recall my high school Math course to build the custom interpolator. After short brainstorming, I took a copybook and drew a graph of functions which illustrates the dependence of the object’s properties with respect to time.

[Custom interpolator]

I wrote three quadratic equations which comply with the graph.

public class GuillotineInterpolator implements TimeInterpolator {

   public static final float ROTATION_TIME = 0.46667f;
   public static final float FIRST_BOUNCE_TIME = 0.26666f;
   public static final float SECOND_BOUNCE_TIME = 0.26667f;

   public GuillotineInterpolator() {

   public float getInterpolation(float t) {
       if (t < ROTATION_TIME) return rotation(t);
       else if (t < ROTATION_TIME + FIRST_BOUNCE_TIME) return firstBounce(t);
       else return secondBounce(t);

   private float rotation(float t) {
       return 4.592f * t * t;

   private float firstBounce(float t) {
       return 2.5f * t * t - 3f * t + 1.85556f;

   private float secondBounce(float t) {
       return 0.625f * t * t - 1.08f * t + 1.458f;

How We Implemented the Action Bar's Rebound

Now our Guillotine menu could fall and make a rebound when it collides with the left border of the screen. However, there was one more rebound I still had to implement. When the Guillotine menu comes back to the initial state, it collides with the action bar producing a bouncing effect. For that, I needed another interpolator.

Here the graph initiates and terminates at 0° angle, but a quadratic dependence is built on the same principle as in the previous case.

public class ActionBarInterpolator implements TimeInterpolator {

   private static final float FIRST_BOUNCE_PART = 0.375f;
   private static final float SECOND_BOUNCE_PART = 0.625f;

   public float getInterpolation(float t) {
       if (t < FIRST_BOUNCE_PART) {
           return (-28.4444f) * t * t + 10.66667f * t;
       } else if (t < SECOND_BOUNCE_PART) {
           return (21.33312f) * t * t - 21.33312f * t + 4.999950f;
       } else {
           return (-9.481481f) * t * t + 15.40741f * t - 5.925926f;

As a result, we’ve got three ObjectAnimation instances: the Guillotine’s opening and shutting, the action bar's rotation, and two interpolators: the Guillotine’s fall and the action bar's rebound. All I needed to do afterwards is set up the interpolations to the appropriate animations, start the action bar's rebound right after shutting the menu, and bind the start of animations with the tap on an appropriate hamburger.

ObjectAnimator rotationAnimator = initAnimator(ObjectAnimator.ofFloat(mGuillotineView, ROTATION, GUILLOTINE_CLOSED_ANGLE, GUILLOTINE_OPENED_ANGLE));



rotationAnimator.addListener(new Animator.AnimatorListener() {...});

That’s it. Building the animation was quite a challenge, but it was totally worth it! Now our smooth Guillotine menu is available for both platforms, iOS and Android.

Read also: How we created FlipViewPager animation for Android

Features Planned

I intend to add a few new effects to the Guillotine menu animation. They include swipe transitions, right-to-left layout support, and a horizontal layout orientation. Stay tuned and watch for our updates.

You can find the sample of the project and its design here:

[Dmytro Denysenko, Android developer at Yalantis]

android ,animation ,mobile ,ui

Published at DZone with permission of Kate Abrosimova. See the original article here.

Opinions expressed by DZone contributors are their own.


Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.


{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}