Menú de botón de acción flotante (FAB) expandible de la biblioteca de soporte de diseño de Android

172

Ahora que la Biblioteca de soporte de diseño de Android está fuera, ¿alguien sabe cómo implementar un menú Fab ampliado con ella, como el fab en la aplicación Inbox?

Debería verse así:

ingrese la descripción de la imagen aquí

EkKoZ
fuente
Ya he revisado toda la documentación, pero aparentemente no hay ningún signo del menú FAB :(
EkKoZ
8
Puede echar un vistazo a esta biblioteca FloatingActionButton .
Markus Rubey
3
@ MarkusRubey gracias, en realidad esa es la que estoy usando en este momento, es solo que quería hacerlo con la nativa, pero aparentemente todavía no es posible.
EkKoZ
Hay muchas bibliotecas de código abierto, que podrían hacer el trabajo. Verifique este: github.com/futuresimple/android-floating-action-button
capt.swag

Respuestas:

143

Actualmente, no se proporciona ningún widget en la Biblioteca de diseño. La única forma de hacerlo de forma rápida y sencilla es mediante el uso de bibliotecas de terceros.

Obviamente, también puede hacer esto usando la Biblioteca de diseño, pero será un trabajo enorme y requerirá mucho tiempo. He mencionado algunas bibliotecas útiles que pueden ayudarlo a lograr esto.

  1. Botón flotante rápido (wangjiegulu)
  2. Botón de acción flotante (clanes)
  3. Botón de acción flotante (makovkastar)
  4. Botón de acción flotante de Android (futuresimple)

Estoy usando el cuarto.

Aritra Roy
fuente
31
bueno, la idea era hacerlo con la biblioteca de diseño nativa, sé que hay muchas bibliotecas que ya cuentan con esta función. gracias por su respuesta.
EkKoZ
¿Encontró algún problema con el cuarto? La mía no aparece en dispositivos lollipop +. Además, el color y el texto de la superposición de fondo no funcionan
Ajith Memana,
@AjithMemana Nope. Mi aplicación está en producción para más de 100K usuarios y nunca tuve ningún problema como usted mencionó.
Aritra Roy
¿Me podría proporcionar un enlace a su aplicación? Necesito algo similar a lo que se muestra arriba, junto con un fondo translúcido que cubre la pantalla cuando se hace clic en el botón.
Ajith Memana
2
Como nota, los clanes y makovkastar ya no son compatibles. Sospecho que esto se debe a las mejoras en la biblioteca de diseño de la FAB
objeciones sinceras del
162

Conseguí un mejor enfoque para implementar el menú FAB de animación sin usar ninguna biblioteca o escribir un enorme código xml para animaciones. Espero que esto ayude en el futuro para alguien que necesita una forma simple de implementar esto.

Simplemente usando la animate().translationY()función, puede animar cualquier vista hacia arriba o hacia abajo como hice en mi código a continuación, verifique el código completo en github . En caso de que esté buscando el mismo código en kotlin, puede consultar el repositorio de código de kotlin Animating FAB Menu .

Primero defina todas sus FAB en el mismo lugar para que se superpongan entre sí, recuerde que la FAB debe ser que desea hacer clic y mostrar otra. p.ej:

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

Ahora en su clase de Java simplemente defina todos sus FAB y realice el clic como se muestra a continuación:

 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 el animation().translationY()para animar su FAB, prefiero que use el atributo de este método en DP ya que solo usar un int afectará la compatibilidad de la pantalla con una resolución más alta o más baja. Como se muestra abajo:

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

Ahora defina la dimensión mencionada anteriormente dentro de res-> valores-> dimens.xml como se muestra a continuación:

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

Eso es todo, espero que esta solución ayude a las personas en el futuro, que están buscando una solución simple.

EDITADO

Si desea agregar una etiqueta sobre el FAB, simplemente tome un LinearLayout horizontal y coloque el FAB con la vista de texto como etiqueta, y anime los diseños si encuentra algún problema al hacer esto, puede verificar mi código de muestra en github, me he encargado de toda la compatibilidad con versiones anteriores problemas en ese código de muestra. compruebe mi código de muestra para FABMenu en Github

para cerrar la FAB en Backpress, anule onBackPress () como se muestra a continuación:

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

La captura de pantalla también tiene el título con el FAB, porque lo tomo de mi aplicación de muestra actual ingithub

ingrese la descripción de la imagen aquí

HAXM
fuente
8
incluso agregar etiquetas al menú también es simple, solo tiene que agregar el FAB dentro de un Linearlayout con algo de vista de texto como etiqueta, y después de eso animar todo el diseño lineal no se olvidó de ocultar y mostrar el diseño lineal dentro de la función de abrir y cerrar
HAXM
1
pero prashant había creado xml por separado para la animación, pero mi solución no necesita ningún xml adicional para la animación, así que creo que mi respuesta es mejor, ya que no necesita una línea de código adicional para animar la vista.
HAXM
2
Esa es la mejor respuesta. Puede usar el FAB real de la biblioteca de diseño y no está tan completo.
Stephane Mathis
3
Lo que también me gustó de este enfoque es que, dado que estamos usando Android FAB, ya se ha hecho mucho trabajo preliminar. por ejemplo, en lugar de escribir el comportamiento de vista personalizada desde cero (ya que las bibliotecas no extendieron FAB), puede extender el comportamiento fabuloso nativo, que es un gran conjunto de código ya disponible
RJFares
1
@droidHeaven toma un framelayout y coloca todo tu FAB dentro de eso
HAXM
31
  • Primero cree los diseños de menú en el archivo xml de su diseño de Actividad. Por ejemplo, un diseño lineal con orientación horizontal e incluye un TextView para la etiqueta y luego un Botón de acción flotante junto al TextView.

  • Cree los diseños de menú según su necesidad y número.

  • Cree un botón de acción flotante base y, al hacer clic, cambie la visibilidad de los diseños de menú.

Por favor, consulte el siguiente código para la referencia y para obtener más información, consulte mi proyecto desde github

Proyecto de pago de 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>

Estas son las animaciones

Animación de apertura del menú 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>

Animación de cierre del menú 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>

Luego, en mi Actividad, simplemente utilicé las animaciones anteriores para mostrar y ocultar el menú FAB:

Mostrar menú fabuloso:

  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;

}

Cerrar menú fabuloso:

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;

}

Aquí está la clase de actividad:

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

Aquí están las capturas de pantalla

Menú de acción flotante con vista de texto de etiqueta en la nueva familia de fuentes cursivas de Android

Menú de acción flotante con vista de texto de etiqueta en la nueva familia de fuentes Roboto de Android

Prasante
fuente
7

Cuando intenté crear algo similar al botón de acción flotante de la bandeja de entrada, pensé en crear un componente personalizado propio.

Sería un diseño de marco simple con altura fija (para contener el menú expandido) que contiene el botón FAB y 3 más colocados debajo de la FAB. cuando haces clic en FAB simplemente animas otros botones para traducir desde debajo de la FAB.

Hay algunas bibliotecas que hacen eso (por ejemplo https://github.com/futuresimple/android-floating-action-button ), pero siempre es más divertido si lo crea usted mismo :)

rwojcik
fuente
Excelente explicación! Estoy explorando esa biblioteca pero tengo problemas para usarla para alinear la FAB entre dos Vistas. Un poco lo que se pregunta en esta pregunta stackoverflow.com/questions/24459352/… . ¿Alguna idea de cómo hacerlo? layout_anchory layout_anchorGravityno están trabajando para mí
acrespo
La biblioteca de Futuresimple no permite un ícono único en el plus que viene por defecto a su elemento FloatingActionMenu
Odaym
7

Puede usar la biblioteca FloatingActionMenu o hacer clic aquí para obtener un tutorial paso a paso. Salida del tutorial:

ingrese la descripción de la imagen aquí

Pacific P. Regmi
fuente
Simple y funciona bien. Sin embargo, sugeriría que agregue los detalles aquí en lugar de vincular el tutorial en caso de que el enlace se caiga en el futuro. Otra cosa, acabo de enterarme de que la biblioteca ya no está en desarrollo, lamentablemente.
Ahmed salah
0

Otra opción para el mismo resultado con la animación ConstraintSet:

ejemplo de animación fabulosa

1) Ponga todas las vistas animadas en un diseño de restricción

2) Anímalo a partir de un código como este (si quieres algunos efectos más, depende de ti ... este es solo un ejemplo)

menuItem1 y menuItem2 es el primer y segundo FAB en menu, descriptionItem1 y descriptionItem2 es la descripción a la izquierda del menú, parentConstraintLayout es la raíz RestraintLayout que contiene todas las vistas animadas, isMenuOpened es alguna función para cambiar la bandera abierta / cerrada en el estado

Puse el código de animación en el archivo de extensión pero no es necesario.

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
fuente