Biblioteka obsługi projektów systemu Android z możliwością rozwijania menu pływającego przycisku akcji (FAB)

172

Teraz, gdy biblioteka wsparcia projektowania Androida jest już dostępna, czy ktoś wie, jak zaimplementować za jej pomocą rozszerzone menu Fab, takie jak fab w aplikacji Inbox?

Powinien wyglądać tak:

wprowadź opis obrazu tutaj

EkKoZ
źródło
Sprawdziłem już całą dokumentację ale najwyraźniej nie ma śladu menu FAB :(
EkKoZ
8
Możesz rzucić okiem na tę bibliotekę FloatingActionButton .
Markus Rubey
3
@MarkusRubey dzięki, właściwie tego właśnie używam w tej chwili, po prostu chciałem zrobić to z rodzimym, ale najwyraźniej nie jest to jeszcze możliwe.
EkKoZ
Istnieje wiele bibliotek open source, które mogą wykonać pracę. Sprawdź to: github.com/futuresimple/android-floating-action-button
capt.swag

Odpowiedzi:

143

Obecnie w Bibliotece projektu nie ma widżetu. Jedynym sposobem, aby to zrobić szybko i łatwo, jest użycie bibliotek innych firm.

Oczywiście możesz to zrobić również za pomocą Biblioteki projektu, ale będzie to ogromna praca i zajmie dużo czasu. Wspomniałem o kilku przydatnych bibliotekach, które mogą Ci w tym pomóc.

  1. Szybki pływający przycisk (wangjiegulu)
  2. Pływający przycisk akcji (klany)
  3. Pływający przycisk akcji (makovkastar)
  4. Pływający przycisk akcji na Androida (futuresimple)

Używam czwartego.

Aritra Roy
źródło
31
cóż, pomysł polegał na zrobieniu tego z natywną biblioteką projektową, wiem, że jest wiele bibliotek, które już obsługują tę funkcję. dzięki za odpowiedź.
EkKoZ
Czy zauważyłeś jakieś problemy z czwartym? Mój nie wyświetla się na urządzeniach Lollipop +. Również kolor nakładki i tekst tła nie działają
Ajith Memana
@AjithMemana Nope. Moja aplikacja jest produkowana dla ponad 100 000 użytkowników i nigdy nie miałem żadnych problemów, o których wspomniałeś.
Aritra Roy
Czy możesz podać mi link do swojej aplikacji? Potrzebuję czegoś podobnego do tego, co pokazano powyżej, wraz z półprzezroczystym tłem zakrywającym ekran po kliknięciu przycisku.
Ajith Memana
2
Uwaga: klany i makovkastar nie są już obsługiwane. Podejrzewam, że jest to spowodowane ulepszeniami w bibliotece projektów FAB
Honest Objections
162

Mam lepsze podejście do zaimplementowania animowanego menu FAB bez użycia żadnej biblioteki lub pisania ogromnego kodu XML dla animacji. mam nadzieję, że pomoże to w przyszłości komuś, kto potrzebuje prostego sposobu na wdrożenie tego.

Używając animate().translationY()funkcji, możesz animować dowolny widok w górę lub w dół, który zrobiłem w poniższym kodzie, sprawdź cały kod na githubie . Jeśli szukasz tego samego kodu w kotlin, możesz sprawdzić repozytorium kodu kotlin Animating FAB Menu .

najpierw zdefiniuj wszystkie swoje FAB w tym samym miejscu, aby zachodziły na siebie, pamiętaj, że na górze FAB powinno być takie, że chcesz kliknąć i pokazać inne. na przykład:

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

Teraz w swojej klasie java po prostu zdefiniuj wszystkie swoje FAB i wykonaj kliknięcie, jak pokazano poniżej:

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

Użyj animation().translationY()do animowania swojego FAB-a, wolę, abyś używał atrybutu tej metody w DP, ponieważ tylko użycie int wpłynie na zgodność wyświetlacza z wyższą lub niższą rozdzielczością. jak pokazano niżej:

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

Teraz zdefiniuj wyżej wymieniony wymiar wewnątrz res-> values-> dimens.xml, jak pokazano poniżej:

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

To wszystko nadzieja, że ​​to rozwiązanie pomoże w przyszłości ludziom, którzy szukają prostego rozwiązania.

EDYTOWANE

Jeśli chcesz dodać etykietę na FAB, po prostu weź poziomy LinearLayout i umieść FAB z tekstem jako etykietą i animuj układy, jeśli znajdziesz jakiś problem, aby to zrobić, możesz sprawdzić mój przykładowy kod na githubie, przygotowałem całą wsteczną kompatybilność problemy w tym przykładowym kodzie. sprawdź mój przykładowy kod dla FABMenu w Github

aby zamknąć FAB na Backpress, zastąp onBackPress (), jak pokazano poniżej:

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

Zrzut ekranu ma również tytuł z FAB, ponieważ wziąłem go z mojej przykładowej aplikacji prezentującej ingithub

wprowadź opis obrazu tutaj

HAXM
źródło
8
nawet dodawanie etykiet do menu jest również proste, wystarczy dodać FAB wewnątrz Linearlayout z jakimś tekstem jako etykietą, a potem animować cały układ liniowy, nie
zapomnij
1
ale prashant stworzył osobny plik XML do animacji, ale moje rozwiązanie nie wymaga dodatkowego kodu XML do animacji, więc uważam, że moja odpowiedź jest lepsza, ponieważ nie wymaga dodatkowej linii kodu do animacji widoku.
HAXM
2
To najlepsza odpowiedź. Możesz użyć prawdziwego FAB z biblioteki projektów i nie jest to aż tak skomplikowane.
Stephane Mathis
3
w tym podejściu podobało mi się również to, że ponieważ używamy Androida FAB, wiele prac przygotowawczych zostało już zrobionych. na przykład zamiast pisać własne zachowanie widoku od zera (ponieważ biblioteki nie rozszerzały FAB), możesz po prostu rozszerzyć natywne zachowanie fab, które jest ogromną ilością już dostępnego kodu
RJFares
1
@droidHeaven zrób sobie framelayout i umieść w nim wszystkie swoje FAB
HAXM
31
  • Najpierw utwórz układy menu w pliku XML układu działania. Na przykład układ liniowy z orientacją poziomą i dołączanie TextView dla etykiety, a następnie pływającego przycisku akcji obok TextView.

  • Utwórz układy menu zgodnie ze swoimi potrzebami i liczbą.

  • Utwórz podstawowy pływający przycisk akcji i po jego kliknięciu zmień widoczność układów menu.

Sprawdź poniższy kod, aby uzyskać odniesienie i więcej informacji, sprawdź mój projekt z github

Projekt kasy z 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>

To są animacje-

Otwieranie animacji 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>

Animacja zamykania 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>

Następnie w mojej aktywności po prostu użyłem animacji powyżej, aby pokazać i ukryć menu FAB:

Pokaż 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;

}

Zamknij 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;

}

Oto klasa Activity -

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

Oto zrzuty ekranu

Pływające menu akcji z widokiem tekstu etykiety w nowej rodzinie czcionek kursywnych systemu Android

Pływające menu akcji z widokiem tekstu etykiety w nowej rodzinie czcionek Roboto dla systemu Android

Prashant
źródło
7

Kiedy próbowałem stworzyć coś podobnego do pływającego przycisku akcji w skrzynce odbiorczej, pomyślałem o stworzeniu własnego komponentu niestandardowego.

Byłby to prosty układ ramek o stałej wysokości (aby zawierał rozwinięte menu) zawierający przycisk FAB i 3 kolejne umieszczone pod FAB. kiedy klikniesz FAB, po prostu animujesz inne przyciski, aby tłumaczyć w górę spod FAB.

Jest kilka bibliotek, które to robią (na przykład https://github.com/futuresimple/android-floating-action-button ), ale zawsze jest fajniej, jeśli tworzysz to samodzielnie :)

rwojcik
źródło
Doskonałe wyjaśnienie! Eksploruję tę bibliotekę, ale mam problem z użyciem jej do wyrównywania FAB między dwoma widokami. Coś o co pytano w tym pytaniu stackoverflow.com/questions/24459352/… . Masz jakiś pomysł, jak to zrobić? layout_anchori layout_anchorGravitynie pracują dla mnie
acrespo
Biblioteka Futuresimple nie pozwala na unikalną ikonę na plusie, która jest domyślnie
przypisana
7

Możesz skorzystać z biblioteki FloatingActionMenu lub kliknąć tutaj, aby przejść do samouczka krok po kroku. Wynik samouczka:

wprowadź opis obrazu tutaj

Pacific P. Regmi
źródło
Proste i działa dobrze. Sugerowałbym jednak dodanie tutaj szczegółów zamiast łączenia z samouczkiem na wypadek, gdyby link zniknął w przyszłości. Kolejna rzecz, właśnie się dowiedziałem, że biblioteka niestety już się nie rozwija.
Ahmed salah
0

Inna opcja dla tego samego wyniku z animacją ConstraintSet:

przykład animacji fab

1) Umieść wszystkie animowane widoki w jednym układzie ConstraintLayout

2) Animuj go z takiego kodu (jeśli chcesz uzyskać więcej efektów, to zależy od ciebie ... to jest tylko przykład)

menuItem1 i menuItem2 to pierwszy i drugi FAB w menu, descriptionItem1 i descriptionItem2 to opis po lewej stronie menu, parentConstraintLayout to główny układ ConstraintLayout, który zawiera wszystkie animowane widoki, isMenuOpened to funkcja do zmiany flagi otwarte / zamknięte w stanie

Umieściłem kod animacji w pliku rozszerzenia, ale nie jest to konieczne.

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
źródło