【问题标题】:Android Design Support Library expandable Floating Action Button(FAB) menuAndroid 设计支持库可扩展的浮动操作按钮(FAB)菜单
【发布时间】:2015-08-30 14:58:35
【问题描述】:

现在 Android 设计支持库已经出来了,有谁知道如何用它实现扩展的 Fab 菜单,比如 Inbox App 上的 fab?

应该是这样的:

【问题讨论】:

标签: android material-design floating-action-button speed-dial


【解决方案1】:

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

只需使用animate().translationY() 函数,您就可以像我在下面的代码check complete code in github 中那样向上或向下动画任何视图。如果您正在 kotlin 中寻找相同的代码,您可以查看 kotlin 代码仓库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->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 中查看我的示例代码,我已经处理了该示例代码中的所有向后兼容性问题。 check my sample code for FABMenu in Github

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

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

屏幕截图的标题和 FAB 一样,因为我是从我的 sample app present ingithub 获取的

【讨论】:

  • 即使在菜单中添加标签也很简单,您只需在带有一些 textview 作为标签的 Linearlayout 中添加 FAB,然后为整个线性布局设置动画,不要忘记隐藏和显示里面的线性布局打开和关闭功能
  • 但是prashant已经为动画制作了单独的xml,但是我的解决方案不需要任何额外的xml来制作动画,所以相信我的答案更好,因为它不需要额外的代码行来制作动画查看。
  • 这是最好的答案。您可以使用设计库中的真正 FAB,而且并不那么复杂。
  • 我还喜欢这种方法的一点是,由于我们使用的是 android FAB,因此已经完成了很多基础工作。例如,无需从头开始编写自定义视图行为(因为库没有扩展 FAB),您可以扩展本机 fab 行为,这是一大堆已经可用的代码
  • @droidHeaven 采用框架布局并将所有 FAB 放入其中
【解决方案2】:
  • 首先在 Activity 布局 xml 文件中创建菜单布局。例如一种 具有水平方向的线性布局,并包含一个 TextView 然后在 TextView 旁边标记一个浮动操作按钮。

  • 根据您的需要和数量创建菜单布局。

  • 创建一个基本浮动操作按钮并在其上 单击该按钮可更改菜单布局的可见性。

请查看以下代码以获取参考,更多信息请查看我的 github 项目

Checkout project from 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 菜单:

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

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

这里是截图

【讨论】:

    【解决方案3】:

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

    这将是简单的框架布局,具有固定高度(包含扩展菜单),包含 FAB 按钮和另外 3 个放置在 FAB 下方。当您点击 FAB 时,您只需为其他按钮设置动画,以便从 FAB 下方向上平移。

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

    【讨论】:

    • 很好的解释!我正在探索该库,但在使用它来对齐两个视图之间的 FAB 时遇到了麻烦。有点像这个问题stackoverflow.com/questions/24459352/…问的问题。关于如何做的任何想法? layout_anchorlayout_anchorGravity 不适合我
    • Futuresimple 的库不允许在加号上使用默认为其 FloatingActionMenu 元素的唯一图标
    • futuresimple 实现大约 5 年没有更新(除了它的许可证)。如果有人仍然需要此功能,我的 ExpandableFab library 是高度可定制、现代且积极维护的。
    【解决方案4】:

    如果有人还在寻找这个功能:我创建了一个 Android 库,它具有这个功能以及更多功能,称为 ExpandableFab (https://github.com/nambicompany/expandable-fab)。

    Material Design 规范将此功能称为 'Speed Dial',ExpandableFab 将其与许多附加功能一起实现。

    几乎所有内容都是可自定义的(颜色、文本、大小、位置、边距、动画等)和可选的(不需要叠加层、FabOptions、标签或图标等)。每个属性都可以通过 XML 布局或以编程方式访问或设置 - 无论您喜欢什么。

    100% 用 Kotlin 编写,但附带完整的 JavaDoc 和 KDoc(已发布的 API 有据可查)。还附带了一个示例应用程序,因此您可以使用 0 编码查看不同的用例。

    Github:https://github.com/nambicompany/expandable-fab

    图书馆网站(带有完整文档的链接):https://nambicompany.github.io/expandable-fab/

    【讨论】:

      【解决方案5】:

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

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

      【讨论】:

        猜你喜欢
        • 2015-08-11
        • 1970-01-01
        • 2023-03-15
        • 1970-01-01
        • 2018-03-04
        • 1970-01-01
        • 2018-10-25
        相关资源
        最近更新 更多