Menu du bouton d'action flottant (FAB) extensible de la bibliothèque de support de conception Android

172

Maintenant que la bibliothèque de support de conception Android est sortie, est-ce que quelqu'un sait comment implémenter le menu Fab étendu avec elle, comme le fab sur l'application Inbox?

Devrait ressembler à ceci:

entrez la description de l'image ici

EkKoZ
la source
J'ai déjà vérifié toute la documentation mais apparemment il n'y a aucun signe du menu FAB :(
EkKoZ
8
Vous pouvez jeter un oeil à cette bibliothèque FloatingActionButton .
Markus Rubey
3
@MarkusRubey merci, en fait c'est celui que j'utilise en ce moment, c'est juste que je voulais le faire avec le natif, mais apparemment ce n'est pas encore possible.
EkKoZ
Il existe de nombreuses bibliothèques open source qui pourraient faire le travail. Vérifiez celui-ci: github.com/futuresimple/android-floating-action-button
capt.swag

Réponses:

143

Actuellement, aucun widget n'est fourni dans la bibliothèque de conception. La seule façon de le faire rapidement et facilement est d'utiliser des bibliothèques tierces.

Vous pouvez évidemment le faire en utilisant la bibliothèque de conception, mais ce sera un travail énorme et nécessitera beaucoup de temps. J'ai mentionné quelques bibliothèques utiles, qui peuvent vous aider à atteindre cet objectif.

  1. Bouton flottant rapide (wangjiegulu)
  2. Bouton d'action flottant (clans)
  3. Bouton d'action flottant (makovkastar)
  4. Bouton d'action flottant Android (futurimple)

J'utilise le 4ème.

Aritra Roy
la source
31
eh bien, l'idée était de le faire avec la bibliothèque de conception native, je sais qu'il y a beaucoup de bibliothèques qui offrent déjà cette fonctionnalité. Merci pour votre réponse.
EkKoZ
Avez-vous trouvé des problèmes avec le quatrième? Le mien n'apparaît pas sur les appareils Lollipop +. De plus, la couleur et le texte de la superposition d'arrière-plan ne fonctionnent pas
Ajith Memana
@AjithMemana Non. Mon application est en production pour plus de 100000 utilisateurs et je n'ai jamais eu de problèmes comme vous l'avez mentionné.
Aritra Roy
Pourriez-vous s'il vous plaît me fournir un lien vers votre application? J'ai besoin de quelque chose de similaire à ce qui est montré ci-dessus, avec un arrière-plan translucide couvrant l'écran lorsque le bouton est cliqué.
Ajith Memana
2
À noter, les clans et makovkastar ne sont plus pris en charge. Je soupçonne que cela est dû à des améliorations dans la bibliothèque de conception FAB
Objections honnêtes
162

Vous avez une meilleure approche pour implémenter le menu FAB animé sans utiliser de bibliothèque ou pour écrire un énorme code XML pour les animations. J'espère que cela aidera à l'avenir pour quelqu'un qui a besoin d'un moyen simple de le mettre en œuvre.

En utilisant simplement la animate().translationY()fonction, vous pouvez animer n'importe quelle vue vers le haut ou vers le bas, comme je l'ai fait dans mon code ci-dessous, vérifiez le code complet dans github . Si vous recherchez le même code dans kotlin, vous pouvez consulter le menu d' animation FAB du repo de code kotlin .

définissez d'abord tous vos FAB au même endroit afin qu'ils se chevauchent, rappelez-vous en haut que le FAB doit être celui que vous voulez cliquer et en afficher les autres. par exemple:

    <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" />

Maintenant, dans votre classe java, définissez simplement tous vos FAB et effectuez le clic comme indiqué ci-dessous:

 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();
            }
        }
    });

Utilisez le animation().translationY()pour animer votre FAB, je préfère que vous utilisiez l'attribut de cette méthode dans DP car seule l'utilisation d'un int affectera la compatibilité d'affichage avec une résolution plus élevée ou une résolution inférieure. comme indiqué ci-dessous:

 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);
}

Définissez maintenant la dimension mentionnée ci-dessus dans res-> values-> dimens.xml comme indiqué ci-dessous:

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

C'est tout l'espoir que cette solution aidera les gens à l'avenir, qui recherchent une solution simple.

ÉDITÉ

Si vous souhaitez ajouter une étiquette sur le FAB, prenez simplement un LinearLayout horizontal et placez le FAB avec textview comme étiquette, et animez les mises en page si vous rencontrez un problème en faisant cela, vous pouvez vérifier mon exemple de code dans github, j'ai géré toute la compatibilité descendante problèmes dans cet exemple de code. vérifier mon exemple de code pour FABMenu dans Github

pour fermer le FAB sur Backpress, remplacez onBackPress () comme indiqué ci-dessous:

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

La capture d'écran a également le titre avec le FAB, car je le prends de mon exemple d'application présent dans

entrez la description de l'image ici

HAXM
la source
8
même l'ajout d'étiquettes au menu est également simple, il vous suffit d'ajouter le FAB dans une mise en page linéaire avec une vue de texte comme étiquette, et après cela, animez toute la mise en page linéaire, n'oubliez pas de masquer et d'afficher la mise en page linéaire dans la fonction d'ouverture et de fermeture
HAXM
1
mais prashant avait créé un xml séparé pour l'animation, mais ma solution n'a pas besoin de xml supplémentaire pour l'animation, alors croyez que ma réponse est meilleure, car elle n'a pas besoin de ligne de code supplémentaire pour animer la vue.
HAXM
2
C'est la meilleure réponse. Vous pouvez utiliser le vrai FAB de la bibliothèque de conception et ce n'est pas si compliqué.
Stephane Mathis
3
ce que j'ai également aimé dans cette approche, c'est que depuis que nous utilisons Android FAB, une grande partie du travail de base a déjà été faite. par exemple, au lieu d'écrire un comportement de vue personnalisé à partir de zéro (puisque les bibliothèques n'ont pas étendu FAB), vous pouvez simplement étendre le comportement de fab natif, qui est un énorme tas de code déjà disponible
RJFares
1
@droidHeaven prenez un framelayout et placez tous vos FAB à l'intérieur
HAXM
31
  • Commencez par créer les dispositions de menu dans le fichier xml de votre disposition d'activité. Par exemple, une mise en page linéaire avec une orientation horizontale et inclure un TextView pour l'étiquette puis un bouton d'action flottant à côté de TextView.

  • Créez les dispositions de menu selon vos besoins et votre numéro.

  • Créez un bouton d'action flottant de base et en cliquant dessus, modifiez la visibilité des dispositions de menu.

Veuillez vérifier le code ci-dessous pour la référence et pour plus d'informations, consultez mon projet depuis github

Commander un projet depuis 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>

Ce sont les animations-

Animation d'ouverture du menu FAB:

<?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>

Animation de fermeture du menu FAB:

<?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>

Ensuite, dans mon activité, j'ai simplement utilisé les animations ci-dessus pour afficher et masquer le menu FAB:

Afficher le menu Fab:

  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;

}

Fermer le menu Fab:

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;

}

Voici la classe d'activité -

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();
}
}

Voici les captures d'écran

Menu d'action flottant avec étiquette Textview dans la nouvelle famille de polices cursives d'Android

Menu d'action flottant avec étiquette Textview dans la nouvelle famille de polices Roboto d'Android

Prashant
la source
7

Lorsque j'ai essayé de créer quelque chose de similaire au bouton d'action flottant de la boîte de réception, j'ai pensé à créer mon propre composant personnalisé.

Ce serait une simple mise en page de cadre avec une hauteur fixe (pour contenir le menu développé) contenant le bouton FAB et 3 autres placés sous le FAB. lorsque vous cliquez sur FAB, vous animez simplement d'autres boutons à traduire sous le FAB.

Il existe des bibliothèques qui font cela (par exemple https://github.com/futuresimple/android-floating-action-button ), mais c'est toujours plus amusant si vous le créez vous-même :)

rwojcik
la source
Excellente explication! J'explore cette bibliothèque mais j'ai du mal à l'utiliser pour aligner le FAB entre deux vues. Kinda ce qu'il est demandé dans cette question stackoverflow.com/questions/24459352/… . Une idée sur la façon de le faire? layout_anchoret layout_anchorGravityne travaillent pas pour moi
acrespo
La bibliothèque de Futuresimple ne permet pas une icône unique sur le plus qui est par défaut son élément FloatingActionMenu
Odaym
7

Vous pouvez utiliser la bibliothèque FloatingActionMenu ou cliquer ici pour un didacticiel étape par étape. Sortie du tutoriel:

entrez la description de l'image ici

Pacific P. Regmi
la source
Simple et fonctionne très bien. Je suggère cependant que vous ajoutiez les détails ici au lieu de lier le didacticiel au cas où le lien tomberait à l'avenir. Autre chose, je viens d'apprendre que la bibliothèque n'est malheureusement plus en développement.
Ahmed salah
3

J'utilise cette bibliothèque pour ce faire: https://github.com/futuresimple/android-floating-action-button

Assez simple à utiliser;)

entrez la description de l'image ici

florent champigny
la source
Certains commentaires que j'ai faits sur les autres réponses similaires. J'ai du mal à l'utiliser pour aligner le FAB entre deux vues. Kinda ce qu'il est demandé dans cette question stackoverflow.com/questions/24459352/… . Une idée sur la façon de le faire? layout_anchoret layout_anchorGravityne travaillent pas pour moi
acrespo
0

Une autre option pour le même résultat avec l'animation ConstraintSet:

exemple d'animation fab

1) Mettez toutes les vues animées dans un seul ConstraintLayout

2) Animez-le à partir d'un code comme celui-ci (si vous voulez plus d'effets, c'est à vous de décider ... ce n'est qu'un exemple)

menuItem1 et menuItem2 sont les premier et deuxième FAB dans le menu, descriptionItem1 et descriptionItem2 est la description à gauche du menu, parentConstraintLayout est la racine ConstraintLayout qui contient toutes les vues animées, isMenuOpened est une fonction pour changer le drapeau ouvert / fermé dans l'état

J'ai mis le code d'animation dans le fichier d'extension mais ce n'est pas nécessaire.

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
la source