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

现在 Android 设计支持库已经出来了,有人知道如何使用它实现扩展的 Fab 菜单吗,比如收件箱应用程序上的 Fab?

应该是这样的:

enter image description here

173105 次浏览

当我尝试创建一些类似于收件箱浮动动作按钮我想创建自己的自定义组件。

这将是一个简单的框架布局与固定高度(包含扩展菜单)包含 FAB 按钮和3个以上的 FAB 下放置。当你点击 FAB,你只是简单地动画其他按钮翻译从下面的 FAB。

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

  • 首先在活动布局 xml 文件中创建菜单布局 具有水平方向的线性布局,包括用于 然后在 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 菜单的开场动画:

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

这里是截图

Floating Action Menu with Label Textview in new Cursive Font Family of Android

Floating Action Menu with Label Textview in new Roboto Font Family of Android

得到了一个更好的方法来实现动画 FAB 菜单,而不使用任何库或为动画编写巨大的 xml 代码。希望这将有助于在未来的人谁需要一个简单的方法来实现这一点。

只要使用 animate().translationY()函数,你可以动画的任何视图向上或向下只是我在我的下面的代码,检查 github 中的完整代码。如果你在 kotlin 中寻找相同的代码,你可以检查 kotlin 代码 repo 动画 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-> value->  沉默. xml 中定义上面提到的维度,如下所示:

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

这就是所有的希望,这个解决方案将帮助人们在未来,谁正在寻找简单的解决方案。

编辑

如果你想在 FAB 上面添加标签,那么只需要一个水平的 LinearLayout,然后把带有 textview 的 FAB 作为标签,如果发现任何问题,你可以在 github 中检查我的样例代码,我已经处理了样例代码中的所有向下兼容问题。检查我在 Github 中的 FABMenu 示例代码

关闭 Backpress 上的 FAB,覆盖 BackPress () ,如下所示:

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

屏幕截图的标题以及与 FAB,因为我采取它从我的 样本应用程序呈现 ingithub

enter image description here

对于 ConstraintSet 动画,相同结果的另一个选项是:

fab animation example

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

2)从这样的代码动画它(如果你想要一些更多的效果它达到 你. . 这只是个例子)

MenuItem1 菜单2是菜单中的第一个和第二个 FABs, 描述项目1 项目2是菜单左侧的描述, ParentConstraintLayout 是包含所有动画视图的根 ConstraintLayout, IsMenuOpen 是在状态中更改打开/关闭标志的函数

我把动画代码在扩展文件,但它不是必要的。

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

如果有人还在寻找这个功能的话: 我开发了一个 Android 库,名为 ExpandableFab (https://github.com/nambicompany/expandable-fab) ,它有这个功能,还有更多功能。

“快速拨号”和 ExpandableFab 实现这个功能时,材料设计规范引用了这个功能以及许多其他的特性。

几乎所有的东西都是可定制的(颜色,文本,大小,位置,边距,动画等等)和可选的(不需要叠加,或 FabOptions,或标签,或图标等等)。每个属性都可以通过 XML 布局或编程方式访问或设置——随您喜欢。

100% 使用 Kotlin 语言编写,但附带完整的 JavaDoc 和 KDoc (已发布的 API 有很好的文档说明)。还附带了一个示例应用程序,所以你可以看到不同的用例与0编码。

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

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

Regular ExpandableFab implementing Material Design 'Speed Dial' functionality A highly customized ExpandableFab implementing Material Design 'Speed Dial' functionality