188

Now that the Android Design Support Library is out, does anyone knows how to implement expanded Fab menu with it, like the fab on Inbox App?

Should look like this:

enter image description here

Roberto Leinardi
  • 10,641
  • 6
  • 65
  • 69
geekkoz
  • 3,654
  • 4
  • 21
  • 19
  • see http://android-developers.blogspot.in/2015/05/android-design-support-library.html – Harin Jun 08 '15 at 06:00
  • I have already check all the documentation but apparently there are not any sign of the FAB menu :( – geekkoz Jun 09 '15 at 17:21
  • 8
    You can take a look at this [FloatingActionButton](https://github.com/futuresimple/android-floating-action-button) library. – Markus Rubey Jun 10 '15 at 10:02
  • 3
    @MarkusRubey thanks, actually thats the one im using at the moment, its just i wanted to make it with the native one, but apparently it´s not possible yet. – geekkoz Jun 11 '15 at 18:24
  • There are lots of open source libraries, which could get the work done. Check this one: https://github.com/futuresimple/android-floating-action-button – capt.swag Jun 15 '15 at 07:40
  • If anything its this one https://github.com/AlexKolpa/fab-toolbar – Eugen Pechanec Jun 15 '15 at 07:45
  • Most same as Google Inbox style I think is: https://github.com/QuadFlask/FloatingActionMenu – mtrakal May 27 '16 at 13:18
  • I am using this library but problem is to set disable other view on menu open like inbox(Google APP). Please Let me know if you have any success with background. https://github.com/futuresimple/android-floating-action-button – ahmad Aug 27 '15 at 13:58
  • By using the future simple, you can get inbox or Evernote fab style http://www.rishabhsinghal.in/implement-floating-action-button-similar-to-inbox-by-gmail-or-evernote/ from here – Sivailango Feb 16 '16 at 18:32
  • None of the libraries in the accepted answer have been updated in years. My [ExpandableFab](https://github.com/nambicompany/expandable-fab) library is highly customizable, modern and maintained. Give it a try. – kabumere Aug 06 '20 at 15:39

5 Answers5

187

Got a better approach to implement the animating FAB menu without using any library or to write huge xml code for animations. hope this will help in future for someone who needs a simple way to implement this.

Just using animate().translationY() function, you can animate any view up or down just I did in my below code, check complete code in github. In case you are looking for the same code in kotlin, you can checkout the kotlin code repo Animating FAB Menu.

first define all your FAB at same place so they overlap each other, remember on top the FAB should be that you want to click and to show other. eg:

    <android.support.design.widget.FloatingActionButton
    android:id="@+id/fab3"
    android:layout_width="@dimen/standard_45"
    android:layout_height="@dimen/standard_45"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/standard_21"
    app:srcCompat="@android:drawable/ic_btn_speak_now" />

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab2"
    android:layout_width="@dimen/standard_45"
    android:layout_height="@dimen/standard_45"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/standard_21"
    app:srcCompat="@android:drawable/ic_menu_camera" />

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab1"
    android:layout_width="@dimen/standard_45"
    android:layout_height="@dimen/standard_45"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/standard_21"
    app:srcCompat="@android:drawable/ic_dialog_map" />

<android.support.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|end"
    android:layout_margin="@dimen/fab_margin"
    app:srcCompat="@android:drawable/ic_dialog_email" />

Now in your java class just define all your FAB and perform the click like shown below:

 FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
    fab1 = (FloatingActionButton) findViewById(R.id.fab1);
    fab2 = (FloatingActionButton) findViewById(R.id.fab2);
    fab3 = (FloatingActionButton) findViewById(R.id.fab3);
    fab.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            if(!isFABOpen){
                showFABMenu();
            }else{
                closeFABMenu();
            }
        }
    });

Use the animation().translationY() to animate your FAB,I prefer you to use the attribute of this method in DP since only using an int will effect the display compatibility with higher resolution or lower resolution. as shown below:

 private void showFABMenu(){
    isFABOpen=true;
    fab1.animate().translationY(-getResources().getDimension(R.dimen.standard_55));
    fab2.animate().translationY(-getResources().getDimension(R.dimen.standard_105));
    fab3.animate().translationY(-getResources().getDimension(R.dimen.standard_155));
}

private void closeFABMenu(){
    isFABOpen=false;
    fab1.animate().translationY(0);
    fab2.animate().translationY(0);
    fab3.animate().translationY(0);
}

Now define the above mentioned dimension inside res->values->dimens.xml as shown below:

    <dimen name="standard_55">55dp</dimen>
<dimen name="standard_105">105dp</dimen>
<dimen name="standard_155">155dp</dimen>

That's all hope this solution will help the people in future, who are searching for simple solution.

EDITED

If you want to add label over the FAB then simply take a horizontal LinearLayout and put the FAB with textview as label, and animate the layouts if find any issue doing this, you can check my sample code in github, I have handelled all backward compatibility issues in that sample code. check my sample code for FABMenu in Github

to close the FAB on Backpress, override onBackPress() as showen below:

    @Override
public void onBackPressed() {
    if(!isFABOpen){
        this.super.onBackPressed();
    }else{
        closeFABMenu();
    }
}

The Screenshot have the title as well with the FAB,because I take it from my sample app present ingithub

enter image description here

HAXM
  • 3,578
  • 4
  • 31
  • 38
  • would have been a great solution for me but unfortunately it's not that simple when you wanna dive more into it, such as adding labels to menu items. This feature is important when a simple icon cannot really describe the action of the fab – usernotnull Nov 24 '16 at 10:04
  • 9
    even adding labels to menu is also simple you just have to add the FAB inside a Linearlayout with some textview as label, and after that animate the whole linear layout dont forgot to hide and show the linear layout inside open and close function – HAXM Nov 24 '16 at 10:14
  • @RJFares I just wanted to make the answer very simple to understand that's why I didn't added the label with the FAB – HAXM Nov 24 '16 at 10:16
  • :) exactly my point. I started doing as you did (the linearlayout with textviews is already presented here by prashant), and kept adding feature over feature until I realized they are already inside one of the 3rd party libraries. But thanks for your input, just wish it was natively added by android. – usernotnull Nov 24 '16 at 10:25
  • 1
    but prashant had made separate xml for animation, but my solution doesn't need any extra xml for animation, so believe my answer is better, as it doesen't need extra line of code for animating the view. – HAXM Nov 24 '16 at 10:30
  • 2
    That's the best answer. You can use the real FAB from the design library and it's not that complecated. – Stephane Mathis Nov 24 '16 at 13:33
  • 3
    what i also liked about this approach is that since we're using android FAB, a lot of the groundwork has already been done. for example instead of writing custom view behavior from scratch (since libraries didn't extend FAB), you can just extend the native fab behavior, which is a huge bunch of code already available – usernotnull Nov 25 '16 at 06:33
  • @HAXM how to close FAB menu when user touch outside FAB area / press back button ? – Zam Sunk Dec 23 '16 at 08:02
  • just override onBackpress menhod and call the closeFABMenu(); method if its open: @Override public void onBackPressed() { if(!isFABOpen){ this.super.onBackPressed(); }else{ closeFABMenu(); } } – HAXM Dec 23 '16 at 09:56
  • This is great. But my fab buttons got placed one above the other (in a column) rather than over the top of each other initially. What could be the reason? – DroidHeaven May 12 '17 at 19:44
  • 1
    @droidHeaven take a framelayout and place all your FAB inside that – HAXM May 12 '17 at 19:50
  • @HAXM Just a little more help required! How to increase margins between FABs. And speed up animation as it is a bit slow. Thanks – Faizan Mubasher Feb 14 '18 at 12:42
  • to increase the margin just increase the dp in dim folder fab1.animate().translationY(-getResources().getDimension(R.dimen.standard_65)); R.dimen.standard_65= 65 dp . same do for all three dimensions 55 to 65 105 =115 and 155=165, to speedup the animation u need to remove the extra hierarchy of layout by using relativeLayout – HAXM Feb 14 '18 at 22:22
  • 1
    @HAXM your example is great! Just one small tip. In case someone taps rapidly on the FAB, its rotation gets distorted. You can fix this by adding this `if (fab.getRotation() != VALUE) { fab.setRotation(VALUE); }` in the listener `onAnimationEnd` for the fab, both in show and close methods. – gts13 Jan 09 '19 at 10:00
  • 1
    @gts13 feel free to add this change to the mentioned github repo, I'll be happy to merge this. thanks – HAXM Jan 09 '19 at 13:33
  • Thank you very much for this answer. It works flawlessly. If people want to animate the main FAB button (from + to x) then use mainFab.animate().rotation(-45F) in showFabMenu() and mainFab.animate().rotation(0) in closeFabMenu() – Deepak kaku Feb 07 '19 at 19:32
  • @FawwazYusran, thanks I have this code in github, reason I didn't convert this to library is it's just few lines of code and if I convert this into a library it will be a separate module and it will contain more code just for the building a completely separate module. – HAXM Jun 15 '19 at 02:45
  • @HAXM Thank you for this idea, i think it is great. My only problem is with the pressing outside this FAB menu. I would like any press outside the menu to close it. I couldn't figure out a solution. Did you manage to solve this? – Andrey Dobrikov Nov 18 '19 at 20:54
  • 1
    @AndreyDobrikov, I haven't think of this, however I can help you with this, just add a view with transparent background or translucent background, and in side `openFAB()` function make it visible and in close make it invisible, and add `onclickListener` to the transparent background to call `closeFAB()` The transparent view should cover entire screen and should be underneath the FABs – HAXM Nov 19 '19 at 09:33
  • @HAXM - yeah i figured it already, I did it with invisible Button. Thanks. – Andrey Dobrikov Nov 19 '19 at 19:10
33
  • First create the menu layouts in the your Activity layout xml file. For e.g. a linear layout with horizontal orientation and include a TextView for label then a Floating Action Button beside the TextView.

  • Create the menu layouts as per your need and number.

  • Create a Base Floating Action Button and on its click of that change the visibility of the Menu Layouts.

Please check the below code for the reference and for more info checkout my project from github

Checkout project from Github

<android.support.constraint.ConstraintLayout
            android:id="@+id/activity_main"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            tools:context="com.app.fabmenu.MainActivity">

            <android.support.design.widget.FloatingActionButton
                android:id="@+id/baseFloatingActionButton"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="16dp"
                android:layout_marginEnd="16dp"
                android:layout_marginRight="16dp"
                android:clickable="true"
                android:onClick="@{FabHandler::onBaseFabClick}"
                android:tint="@android:color/white"
                app:fabSize="normal"
                app:layout_constraintBottom_toBottomOf="@+id/activity_main"
                app:layout_constraintRight_toRightOf="@+id/activity_main"
                app:srcCompat="@drawable/ic_add_black_24dp" />

            <LinearLayout
                android:id="@+id/shareLayout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="12dp"
                android:layout_marginEnd="24dp"
                android:layout_marginRight="24dp"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:visibility="invisible"
                app:layout_constraintBottom_toTopOf="@+id/createLayout"
                app:layout_constraintLeft_toLeftOf="@+id/createLayout"
                app:layout_constraintRight_toRightOf="@+id/activity_main">

                <TextView
                    android:id="@+id/shareLabelTextView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="8dp"
                    android:layout_marginRight="8dp"
                    android:background="@drawable/shape_fab_label"
                    android:elevation="2dp"
                    android:fontFamily="sans-serif"
                    android:padding="5dip"
                    android:text="Share"
                    android:textColor="@android:color/white"
                    android:typeface="normal" />


                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/shareFab"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:clickable="true"
                    android:onClick="@{FabHandler::onShareFabClick}"
                    android:tint="@android:color/white"
                    app:fabSize="mini"
                    app:srcCompat="@drawable/ic_share_black_24dp" />

            </LinearLayout>

            <LinearLayout
                android:id="@+id/createLayout"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginBottom="24dp"
                android:layout_marginEnd="24dp"
                android:layout_marginRight="24dp"
                android:gravity="center_vertical"
                android:orientation="horizontal"
                android:visibility="invisible"
                app:layout_constraintBottom_toTopOf="@+id/baseFloatingActionButton"
                app:layout_constraintRight_toRightOf="@+id/activity_main">

                <TextView
                    android:id="@+id/createLabelTextView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginEnd="8dp"
                    android:layout_marginRight="8dp"
                    android:background="@drawable/shape_fab_label"
                    android:elevation="2dp"
                    android:fontFamily="sans-serif"
                    android:padding="5dip"
                    android:text="Create"
                    android:textColor="@android:color/white"
                    android:typeface="normal" />

                <android.support.design.widget.FloatingActionButton
                    android:id="@+id/createFab"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:clickable="true"
                    android:onClick="@{FabHandler::onCreateFabClick}"
                    android:tint="@android:color/white"
                    app:fabSize="mini"
                    app:srcCompat="@drawable/ic_create_black_24dp" />

            </LinearLayout>

        </android.support.constraint.ConstraintLayout>

These are the animations-

Opening animation of FAB Menu:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
    android:duration="300"
    android:fromXScale="0"
    android:fromYScale="0"
    android:interpolator="@android:anim/linear_interpolator"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="1"
    android:toYScale="1" />
<alpha
    android:duration="300"
    android:fromAlpha="0.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="1.0" />

</set>

Closing animation of FAB Menu:

<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:fillAfter="true">
<scale
    android:duration="300"
    android:fromXScale="1"
    android:fromYScale="1"
    android:interpolator="@android:anim/linear_interpolator"
    android:pivotX="50%"
    android:pivotY="50%"
    android:toXScale="0.0"
    android:toYScale="0.0" />
<alpha
    android:duration="300"
    android:fromAlpha="1.0"
    android:interpolator="@android:anim/accelerate_interpolator"
    android:toAlpha="0.0" />
</set>

Then in my Activity I've simply used the animations above to show and hide the FAB menu :

Show Fab Menu:

  private void expandFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(45.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabOpenAnimation);
    binding.shareLayout.startAnimation(fabOpenAnimation);
    binding.createFab.setClickable(true);
    binding.shareFab.setClickable(true);
    isFabMenuOpen = true;

}

Close Fab Menu:

private void collapseFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(0.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabCloseAnimation);
    binding.shareLayout.startAnimation(fabCloseAnimation);
    binding.createFab.setClickable(false);
    binding.shareFab.setClickable(false);
    isFabMenuOpen = false;

}

Here is the the Activity class -

package com.app.fabmenu;

import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.design.widget.Snackbar;
import android.support.v4.view.ViewCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.OvershootInterpolator;

import com.app.fabmenu.databinding.ActivityMainBinding;

public class MainActivity extends AppCompatActivity {

private ActivityMainBinding binding;
private Animation fabOpenAnimation;
private Animation fabCloseAnimation;
private boolean isFabMenuOpen = false;

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

    binding = DataBindingUtil.setContentView(this,    R.layout.activity_main);
    binding.setFabHandler(new FabHandler());

    getAnimations();


}

private void getAnimations() {

    fabOpenAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_open);

    fabCloseAnimation = AnimationUtils.loadAnimation(this, R.anim.fab_close);

}

private void expandFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(45.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabOpenAnimation);
    binding.shareLayout.startAnimation(fabOpenAnimation);
    binding.createFab.setClickable(true);
    binding.shareFab.setClickable(true);
    isFabMenuOpen = true;


}

private void collapseFabMenu() {

    ViewCompat.animate(binding.baseFloatingActionButton).rotation(0.0F).withLayer().setDuration(300).setInterpolator(new OvershootInterpolator(10.0F)).start();
    binding.createLayout.startAnimation(fabCloseAnimation);
    binding.shareLayout.startAnimation(fabCloseAnimation);
    binding.createFab.setClickable(false);
    binding.shareFab.setClickable(false);
    isFabMenuOpen = false;

}


public class FabHandler {

    public void onBaseFabClick(View view) {

        if (isFabMenuOpen)
            collapseFabMenu();
        else
            expandFabMenu();


    }

    public void onCreateFabClick(View view) {

        Snackbar.make(binding.coordinatorLayout, "Create FAB tapped", Snackbar.LENGTH_SHORT).show();

    }

    public void onShareFabClick(View view) {

        Snackbar.make(binding.coordinatorLayout, "Share FAB tapped", Snackbar.LENGTH_SHORT).show();

    }


}

@Override
public void onBackPressed() {

    if (isFabMenuOpen)
        collapseFabMenu();
    else
        super.onBackPressed();
}
}

Here are the screenshots

Floating Action Menu with Label Textview in new Cursive Font Family of Android

Floating Action Menu with Label Textview in new Roboto Font Family of Android

Prashant
  • 1,046
  • 14
  • 21
7

When I tried to create something simillar to inbox floating action button i thought about creating own custom component.

It would be simple frame layout with fixed height (to contain expanded menu) containing FAB button and 3 more placed under the FAB. when you click on FAB you just simply animate other buttons to translate up from under the FAB.

There are some libraries which do that (for example https://github.com/futuresimple/android-floating-action-button), but it's always more fun if you create it by yourself :)

rwojcik
  • 1,030
  • 9
  • 18
  • Excellent explanation! I'm exploring that library but I'm having trouble using it to align the FAB between two Views. Kinda what it is asked in this question http://stackoverflow.com/questions/24459352/how-can-i-add-the-new-floating-action-button-between-two-widgets-layouts. Any idea on how to do it? ```layout_anchor``` and ```layout_anchorGravity``` are not working for me – acrespo Aug 06 '15 at 14:20
  • Futuresimple's library doesn't allow for a unique icon on the plus that comes default to its FloatingActionMenu element – Odaym Nov 22 '15 at 18:21
  • The futuresimple implementation hasn't been updated in about 5 years (apart from its License). If anyone still needs this functionality, my [ExpandableFab library](https://github.com/nambicompany/expandable-fab) is highly customizable, modern and actively maintained. – kabumere Oct 06 '20 at 01:50
7

In case anyone is still looking for this functionality: I made an Android library that has this ability and much more, called ExpandableFab (https://github.com/nambicompany/expandable-fab).

The Material Design spec refers to this functionality as 'Speed Dial' and ExpandableFab implements it along with many additional features.

Nearly everything is customizable (colors, text, size, placement, margins, animations and more) and optional (don't need an Overlay, or FabOptions, or Labels, or icons, etc). Every property can be accessed or set through XML layouts or programmatically - whatever you prefer.

Written 100% in Kotlin but comes with full JavaDoc and KDoc (published API is well documented). Also comes with an example app so you can see different use cases with 0 coding.

Github: https://github.com/nambicompany/expandable-fab

Library website (w/ links to full documentation): https://nambicompany.github.io/expandable-fab/

Regular ExpandableFab implementing Material Design 'Speed Dial' functionality A highly customized ExpandableFab implementing Material Design 'Speed Dial' functionality

kabumere
  • 353
  • 2
  • 8
1

Another option for the same result with ConstraintSet animation:

fab animation example

1) Put all the animated views in one ConstraintLayout

2) Animate it from code like this (if you want some more effects its up to you..this is only example)

menuItem1 and menuItem2 is the first and second FABs in menu, descriptionItem1 and descriptionItem2 is the description to the left of menu, parentConstraintLayout is the root ConstraintLayout wich contains all the animated views, isMenuOpened is some function to change open/closed flag in the state

I put animation code in extension file but its not necessary.

fun FloatingActionButton.expandMenu(
    menuItem1: View,
    menuItem2: View,
    descriptionItem1: TextView,
    descriptionItem2: TextView,
    parentConstraintLayout: ConstraintLayout,
    isMenuOpened: (Boolean)-> Unit
) {
    val constraintSet = ConstraintSet()
    constraintSet.clone(parentConstraintLayout)

    constraintSet.setVisibility(descriptionItem1.id, View.VISIBLE)
    constraintSet.clear(menuItem1.id, ConstraintSet.TOP)
    constraintSet.connect(menuItem1.id, ConstraintSet.BOTTOM, this.id, ConstraintSet.TOP, 0)
    constraintSet.connect(menuItem1.id, ConstraintSet.START, this.id, ConstraintSet.START, 0)
    constraintSet.connect(menuItem1.id, ConstraintSet.END, this.id, ConstraintSet.END, 0)

    constraintSet.setVisibility(descriptionItem2.id, View.VISIBLE)
    constraintSet.clear(menuItem2.id, ConstraintSet.TOP)
    constraintSet.connect(menuItem2.id, ConstraintSet.BOTTOM, menuItem1.id, ConstraintSet.TOP, 0)
    constraintSet.connect(menuItem2.id, ConstraintSet.START, this.id, ConstraintSet.START, 0)
    constraintSet.connect(menuItem2.id, ConstraintSet.END, this.id, ConstraintSet.END, 0)

    val transition = AutoTransition()
    transition.duration = 150
    transition.interpolator = AccelerateInterpolator()

    transition.addListener(object: Transition.TransitionListener {
        override fun onTransitionEnd(p0: Transition) {
            isMenuOpened(true)
        }
        override fun onTransitionResume(p0: Transition) {}
        override fun onTransitionPause(p0: Transition) {}
        override fun onTransitionCancel(p0: Transition) {}
        override fun onTransitionStart(p0: Transition) {}
    })

    TransitionManager.beginDelayedTransition(parentConstraintLayout, transition)
    constraintSet.applyTo(parentConstraintLayout)
}
Konstantin Kuznetsov
  • 1,188
  • 10
  • 13