Android设计支持库可扩展的浮动操作按钮(FAB)菜单


172

既然Android设计支持库已经发布,有谁知道如何使用它来实现扩展的Fab菜单,例如Inbox App上的fab?

应该看起来像这样:

在此处输入图片说明



我已经检查了所有文档,但显然没有FAB菜单的任何迹象:(
EkKoZ 2015年

8
您可以看一下这个FloatingActionButton库。
Markus Rubey 2015年

3
@MarkusRubey谢谢,实际上多数民众赞成在当前使用的是那个即时消息,它只是我想与本地即时消息一起使用,但显然这还不可能。
EkKoZ

有很多开源库可以完成工作。请检查以下内容:github.com/futuresimple/android-floating-action-button
capt.swag

Answers:


143

当前,设计库中没有提供任何小部件。快速而轻松地执行此操作的唯一方法是使用第三方库。

您显然也可以使用设计库来执行此操作,但这将是一项巨大的工作,并且需要大量时间。我已经提到了一些有用的库,可以帮助您实现这一目标。

  1. 快速浮动按钮(wangjiegulu)
  2. 浮动动作按钮(氏族)
  3. 浮动动作按钮(makovkastar)
  4. Android浮动操作按钮(futuresimple)

我正在使用第四个。


31
好吧,这个想法是使用本机设计库来完成的,我知道已经有很多库已经实现了此功能。感谢您的回复。
EkKoZ

您发现第四个问题吗?我的没有出现在棒棒糖+设备上。此外,背景叠加层的颜色和文本不起作用
Ajith Memana

@AjithMemana Nope。我的应用程序已面向超过10万名用户投入生产,您从未提到过任何问题。
阿里特拉·罗伊

您能给我一个链接到您的应用程序吗?我需要类似于上面显示的内容,以及单击按钮时覆盖屏幕的半透明背景。
Ajith Memana '16

2
请注意,不再支持氏族和makovkastar。我怀疑这是由于FAB设计库的改进
诚实的反对意见

162

获得了一种更好的方法来实现动画FAB菜单,而无需使用任何库或为动画编写巨大的xml代码。希望这对将来需要一个简单方法来实现此目标的人有所帮助。

只需使用animate().translationY()函数,就可以像我在下面的代码中那样向上或向下动画任何视图,请在github中检查完整的代码。如果您在kotlin中查找相同的代码,则可以签出kotlin代码回购Animating FAB Menu

首先在同一位置定义所有FAB,以便它们彼此重叠,请记住,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-> dimens.xml中定义上述维度,如下所示:

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

所有人都希望该解决方案能够为将来寻求简单解决方案的人们提供帮助。

已编辑

如果要在FAB上添加标签,则只需采用水平的LinearLayout并将FAB以textview作为标签,并在布局上设置动画,如果发现这样做有任何问题,则可以在github中检查我的示例代码,我已经实现了所有向后兼容性该示例代码中的问题。检查我在Github中的FABMenu的示例代码

要在Backpress上关闭FAB,请覆盖onBackPress(),如下所示:

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

屏幕快照也带有FAB的标题,因为我是从github上的示例应用程序中获取的

在此处输入图片说明


8
甚至向菜单添加标签也很简单,您只需要在带有一些textview作为标签的Linearlayout中添加FAB,然后为整个线性布局设置动画,就不要忘记隐藏和显示打开和关闭功能中的线性布局了
HAXM

1
但是prashant为动画制作了单独的xml,但是我的解决方案不需要为动画制作任何额外的xml,因此相信我的回答会更好,因为它不需要额外的代码来动画化视图。
HAXM

2
那是最好的答案。您可以使用设计库中的真实FAB,而且功能并不复杂。
Stephane Mathis

3
我也喜欢这种方法的原因是,由于我们使用的是Android FAB,因此已经完成了许多基础工作。例如,与其从头开始编写自定义视图行为(因为库没有扩展FAB),您可以扩展本机fab行为,这是一大堆已经可用的代码
RJFares

1
@droidHeaven进行框架布局,并将所有FAB放入其中
-HAXM

31
  • 首先在“活动布局” xml文件中创建菜单布局。例如,具有水平方向的线性布局,并包含用于标签的TextView,然后在TextView旁边包含一个浮动动作按钮。

  • 根据您的需要和编号创建菜单布局。

  • 创建一个基本浮动操作按钮,然后单击该按钮以更改菜单布局的可见性。

请检查以下代码以供参考,有关更多信息,请从github检出我的项目

来自Github的Checkout项目

<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菜单的打开动画:

<?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菜单的关闭动画:

<?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菜单:

显示工厂菜单:

  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;

}

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

这是屏幕截图

新的Android草书字体家族中带有标签Textview的浮动操作菜单

新的Android Roboto字体家族中带有标签Textview的浮动操作菜单



7

当我尝试创建类似于收件箱浮动操作按钮的东西时,我想到了创建自己的自定义组件。

这将是具有固定高度(包含扩展菜单)的简单框架布局,其中包含FAB按钮,并在FAB下方放置3个以上按钮。当您单击FAB时,您只需为其他按钮设置动画即可从FAB下方向上转换。

有一些库可以做到这一点(例如https://github.com/futuresimple/android-floating-action-button),但是如果您自己创建它,总会更加有趣:)


极好的解释!我正在探索该库,但是使用它在两个视图之间对齐FAB时遇到了麻烦。请在此问题stackoverflow.com/questions/24459352/…中询问什么。有什么想法吗?layout_anchorlayout_anchorGravity不是为我工作
acrespo,2015年

Futuresimple的库不允许在加号上使用唯一的图标,该图标默认为其FloatingActionMenu元素
Odaym 2015年

7

您可以使用FloatingActionMenu库,或单击此处获取分步教程。本教程的输出:

在此处输入图片说明


简单,工作正常。我建议您在此处添加详细信息,而不是链接本教程,以防将来链接断开。不幸的是,我刚刚了解到该库不再处于开发中。
艾哈迈德·萨拉


0

使用ConstraintSet动画获得相同结果的另一个选项:

fab动画示例

1)将所有动画视图放在一个ConstraintLayout中

2)从这样的代码中对其进行动画处理(如果您想要更多效果取决于您自己,这只是示例)

menuItem1menuItem2是菜单中的第一和第二个FAB, descriptionItem1descriptionItem2是菜单左侧的描述, 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)
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.