Android Design Support Library expandable Floating Action Button(FAB) menu

后端 未结 5 931
耶瑟儿~
耶瑟儿~ 2020-11-28 17:26

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:

相关标签:
5条回答
  • 2020-11-28 17:52

    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 :)

    0 讨论(0)
  • 2020-11-28 17:53

    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

    0 讨论(0)
  • 2020-11-28 17:59

    Another option for the same result with ConstraintSet animation:

    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)
    }
    
    0 讨论(0)
  • 2020-11-28 18:06

    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

    0 讨论(0)
  • 2020-11-28 18:08
    • 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

    0 讨论(0)
提交回复
热议问题