Расширяемая библиотека библиотеки поддержки Android - меню с плавающей кнопкой действий (FAB)

172

Теперь, когда отсутствует библиотека поддержки дизайна Android, кто-нибудь знает, как реализовать расширенное меню Fab с его помощью, например, fab на Inbox App?

Должно выглядеть так:

введите описание изображения здесь

EkKoZ
источник
Я уже проверил всю документацию, но, видимо, нет никаких признаков меню FAB :(
EkKoZ
8
Вы можете взглянуть на эту библиотеку FloatingActionButton .
Маркус Рубей
3
@MarkusRubey спасибо, на самом деле это тот, который я сейчас использую, просто я хотел сделать это с родным, но, видимо, пока это невозможно.
EkKoZ
Есть много библиотек с открытым исходным кодом, которые могут сделать работу. Проверьте это: github.com/futuresimple/android-floating-action-button
capt.swag

Ответы:

143

В настоящее время в Библиотеке дизайна нет виджетов. Единственный способ сделать это быстро и легко - использовать сторонние библиотеки.

Очевидно, что вы можете сделать это и с помощью Библиотеки дизайна, но это будет огромная работа и потребует много времени. Я упомянул несколько полезных библиотек, которые могут помочь вам достичь этого.

  1. Быстрая Плавающая Кнопка (wangjiegulu)
  2. Кнопка с плавающим действием (кланы)
  3. Кнопка с плавающим действием (маковкастар)
  4. Плавающая кнопка Android (futuresimple)

Я использую 4-й.

Аритра Рой
источник
31
Ну, идея состояла в том, чтобы сделать это с собственной библиотекой дизайна, я знаю, что есть много библиотек, которые уже делают эту функцию. Спасибо за ваш ответ.
EkKoZ
Вы нашли какие-либо проблемы с четвертым? Мой не появляется на устройствах Lollipop +. Также не работает цвет наложения фона и текста
Аджит Мемана
@AjithMemana Нет. Мое приложение работает более чем с 100 тысячами пользователей, и у меня никогда не было проблем, о которых вы упоминали.
Аритра Рой
Не могли бы вы предоставить мне ссылку на ваше приложение? Мне нужно что-то похожее на то, что показано выше, вместе с полупрозрачным фоном, покрывающим экран при нажатии кнопки.
Аджит Мемана
2
Как примечание, кланы и маковкастар больше не поддерживаются. Я подозреваю, что это связано с улучшениями в библиотеке дизайна FAB
Честные возражения,
162

Получил лучший подход для реализации анимационного меню FAB без использования какой-либо библиотеки или для написания огромного XML-кода для анимации. надеюсь, это поможет в будущем тому, кому нужен простой способ реализовать это.

Просто используя animate().translationY()функцию, вы можете анимировать любое представление вверх или вниз, как я сделал в своем коде ниже, проверьте полный код в github . Если вы ищете тот же код в kotlin, вы можете проверить kolin код репо Animating FAB Menu .

сначала определите все ваши FAB в одном и том же месте, чтобы они перекрывали друг друга, помните, что в верхней части FAB должно быть то, что вы хотите щелкнуть и показать другие. например:

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

Теперь в вашем Java-классе просто определите все ваши FAB и выполните щелчок, как показано ниже:

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

Используйте animation().translationY()для анимации вашего FAB, я предпочитаю использовать атрибут этого метода в DP, так как только использование int повлияет на совместимость дисплея с более высоким или более низким разрешением. как показано ниже:

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

Теперь определите вышеупомянутое измерение внутри res-> values-> dimensions.xml, как показано ниже:

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

Это все надежда, что это решение поможет людям в будущем, которые ищут простое решение.

отредактированный

Если вы хотите добавить метку поверх FAB, то просто возьмите горизонтальную LinearLayout и поместите FAB с textview в качестве метки, и анимируйте макеты, если обнаружите какую-либо проблему, делающую это, вы можете проверить мой пример кода в github, я обработал всю обратную совместимость проблемы в этом примере кода. проверьте мой пример кода для FABMenu в Github

чтобы закрыть FAB на Backpress, переопределите onBackPress (), как показано ниже:

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

Снимок экрана также имеет название с FAB, потому что я взял его из моего примера приложения в ingithub

введите описание изображения здесь

HAXM
источник
8
Даже добавление меток в меню также просто, вам просто нужно добавить FAB внутри Linearlayout с некоторым текстовым представлением в качестве метки, и после этого анимировать весь линейный макет, не забывая скрыть и показать линейный макет внутри функции открытия и закрытия
HAXM
1
но prashant создал отдельный xml для анимации, но мое решение не нуждается в дополнительном xml для анимации, поэтому поверьте, мой ответ лучше, так как ему не нужна дополнительная строка кода для анимации представления.
HAXM
2
Это лучший ответ. Вы можете использовать настоящий FAB из библиотеки дизайна, и он не настолько завершен.
Стефан Матис
3
Что мне также понравилось в этом подходе, так это то, что, поскольку мы используем Android FAB, многое уже сделано. например, вместо того, чтобы писать собственное поведение представления с нуля (поскольку библиотеки не расширяли FAB), вы можете просто расширить собственное поведение fab, которое уже является огромной связкой кода
RJFares
1
@droidHeaven возьмите фрейм-макет и поместите в него все свои FAB
HAXM
31
  • Сначала создайте макеты меню в XML-файле вашего макета «Активность». Например, для линейного макета с горизонтальной ориентацией и включения TextView для надписи, затем кнопка плавающего действия рядом с TextView.

  • Создайте макеты меню в соответствии с вашими потребностями и номером.

  • Создайте кнопку базового плавающего действия и по ее нажатию измените видимость макетов меню.

Пожалуйста, проверьте приведенный ниже код для справки и для получения дополнительной информации оформите мой проект на github.

Оформить заказ от 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>

Это анимации

Открытие анимации 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>

Закрытие анимации 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>

Затем в своей деятельности я просто использовал приведенные выше анимации, чтобы показать и скрыть меню FAB:

Показать 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;

}

Закрыть 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;

}

Вот класс 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();
}
}

Вот скриншоты

Плавающее меню действий с надписью TextView в новом семействе Cursive Font для Android

Плавающее меню действий с надписью TextView в новом семействе роботов Roboto для Android

Prashant
источник
7

Когда я попытался создать что-то похожее на кнопку «Входящие», я подумал о создании собственного пользовательского компонента.

Это был бы простой макет кадра с фиксированной высотой (чтобы содержать расширенное меню), содержащий кнопку FAB и еще 3, расположенные под FAB. когда вы нажимаете на FAB, вы просто анимируете другие кнопки для перевода из-под FAB.

Есть некоторые библиотеки, которые делают это (например, https://github.com/futuresimple/android-floating-action-button ), но всегда веселее, если вы создаете их самостоятельно :)

rwojcik
источник
Отличное объяснение! Я изучаю эту библиотеку, но у меня возникают проблемы с ее использованием для выравнивания FAB между двумя представлениями. Вроде то, что задают в этом вопросе stackoverflow.com/questions/24459352/… . Есть идеи, как это сделать? layout_anchorи layout_anchorGravityне работают на меня
acrespo
Библиотека Futuresimple не позволяет использовать уникальный значок плюса, который по умолчанию
присваивается
7

Вы можете использовать библиотеку FloatingActionMenu или щелкнуть здесь для пошагового руководства. Выход из учебника:

введите описание изображения здесь

Pacific P. Regmi
источник
Просто и отлично работает. Я бы посоветовал добавить детали здесь вместо ссылки на учебник на случай, если ссылка выйдет из строя в будущем. Другое дело, я только что узнал, что библиотека больше не находится в разработке, к сожалению.
Ахмед Салах
3

Я использую эту библиотеку, чтобы сделать это: https://github.com/futuresimple/android-floating-action-button

Довольно прост в использовании;)

введите описание изображения здесь

Флорент Шампиньи
источник
Некоторый комментарий, который я сделал к другим подобным ответам. У меня проблемы с его использованием для выравнивания FAB между двумя представлениями. Вроде то, что задают в этом вопросе stackoverflow.com/questions/24459352/… . Есть идеи, как это сделать? layout_anchorи layout_anchorGravityне работают на меня
acrespo
0

Еще один вариант для того же результата с анимацией ConstraintSet:

потрясающий пример анимации

1) Поместите все анимированные представления в один ConstraintLayout

2) Анимируйте его из кода, подобного этому (если вы хотите, чтобы некоторые дополнительные эффекты были на ваше усмотрение ... это только пример)

menuItem1 и menuItem2 - это первый и второй FAB в меню, descriptionItem1 и descriptionItem2 - описание слева от меню, parentConstraintLayout - корневой ConstraintLayout, содержащий все анимированные представления, isMenuOpened - это некоторая функция для изменения флага открытия / закрытия в состоянии

Я поместил анимационный код в файл расширения, но это не обязательно.

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)
}
Константин Кузнецов
источник