Guillotine Menu Animation for Android: Developing Process and Challenges We’ve Faced

Hopefully, you’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 – the Guillotine Menu (If you haven’t read about it yet then click here to open the story in a new tab! Also, you can see the iOS animation on Dribbble and GitHub). Soon after developing the Guillotine menu for iOS, our Android developer Dmytro Denysenko took up the challenge to implement the same animation 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 a solution.

Developing the Guillotine Menu Animation for Android

by Dmytro Denysenko

Where to start?

At first, I wanted to resort to standard solutions to implement the Guillotine menu component for Android. After all, it seemed possible at the beginning! 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: the roubound of a soccer ball, hardly that 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 the Guillotine's rotation, the Guillotine's rebound, and the 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: 1) find the centre of rotation, and 2) 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 made it even with the center of 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 that was customized with the help of elasticity and resistance properties. However, it isn’t that easy on Android.

standard android interpolator

[Standard Android interpolator]

As I mentioned earlier, I could use a default BounceInterpolator for the layout rotation but it appeared to give too soft of a 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 a 0 to 1 ratio. In my case, the angle of rotation goes from 0° to 90° (clockwise). This means that at 0° 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. I had to recall my high school math course to build the custom interpolator. After some quick brainstorming, I took a copybook and drew a graph of functions to illustrate the dependence of the object’s properties with respect to time.

custom interpolator

[Custom interpolator]

I wrote three quadratic equations which comply with the graph.

Quadratic equations

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 collided 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 a 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 was to 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

Planned features

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:

4.6/ 5.0
Article rating
Remember those Facebook reactions? Well, we aren't Facebook but we love reactions too. They can give us valuable insights on how to improve what we're doing. Would you tell us how you feel about this article?
Excited to create something outstanding?

We share the same interests.

Contact us

We use cookies to personalize our service and to improve your experience on the website and its subdomains. We also use this information for analytics.