中文翻译:
http://wiki.jikexueyuan.com/project/material-design/components/buttons-floating-action-button.html
概述
浮动操作按钮 (简称 FAB) 是一个特殊的promoted(进阶)操作案例,因为一个浮动在UI之上的圆形图标而显得格外突出,同时它还具有动态的效果,比如变形、弹出、位移等。
- 不是每个屏幕都需要浮动操作按钮,一个屏幕只推荐使用一个浮动操作按钮来增加其显著性,它应该只代表最常见的操作。
- 用浮动操作按钮进行积极的操作,如创建、收藏、分享、浏览和探索。避免用浮动操作按钮进行轻微破坏性操作。
- 浮动操作按钮不包含 app 图标或状态栏通知。不要把证章或者其他元素层叠在浮动操作按钮上。
- APP 主要操作的图案强制使用圆形图标。
- 不要给浮动操作按钮额外的维度。
- 在默认情况下,浮动操作按钮就像一块扩展的物体在屏幕上活动。在它里面的图标可能是活动的。
……里面列出了几十种使用规范,但是却没有一行代码
基本使用
继承关系
public class FloatingActionButton extends VisibilityAwareImageButton
class VisibilityAwareImageButton extends ImageButton
放置浮动操作按钮需要使用CoordinatorLayout。
CoordinatorLayout可以让一个元素浮动在另一个元素之上,还
帮助我们协调它所包含的子view之间的交互,比如当用户往下滚动一个页面,浮动操作按钮应该消失,一旦向上滚动,则重现。
另外,由于CoordinatorLayout
不支持
和ListView一起
工作
,所以
需要用RecyclerView来替换ListViews。同时你还必须把RecyclerView升级到v22版本,之前的v21同样不支持与CoordinatorLayout一起工作。
案例:
<android.support.design.widget.FloatingActionButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:backgroundTint="#00f"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end"
app:layout_behavior="com.example.administrator.myapplication.ScrollAwareFABBehavior"
app:srcCompat="@android:drawable/ic_dialog_email"/>
其中:
- app:backgroundTint:按钮的背景颜色,默认使用theme中colorAccent的颜色
- app:layout_anchor:指定参照物
- app:layout_anchorGravity:指定相对于参照物的位置
- app:layout_behavior:设置FAB自定义显示隐藏动画效果
- app:srcCompat或android:src:设置浮动按钮的图标
其他相关属性:
- app:fabSize:按钮是正常大小还是小号 Size for the FAB.
- app:rippleColor:点击的边缘阴影颜色,默认取的是theme中的colorControlHighlight
- app:useCompatPadding:是否启用 Enable compat padding.
- app:borderWidth:边框宽度。通常设置为0,同时设置一个合理的margin(16dp),可以解决Android 5.X设备上阴影无法正常显示的问题
- app:elevation:和立体感相关的属性,设置边缘阴影的宽度 Elevation value for the FAB
- app:pressedTranslationZ:和立体感相关的属性,设置点击按钮时,按钮边缘阴影的宽度,通常设置比elevation的数值大
根据谷歌的设计规范,drawable的尺寸应该是24dp,
按钮应该处于屏幕的右下角,margin设置为16dp。
FAB与Snackbar
当Snackbar在显示的时候,往往出现在屏幕的底部。为了给Snackbar留出空间,浮动操作按钮需要向上移动。
只要使用
CoordinatorLayout作为基本布局,将自动产生向上移动的动画。
浮动操作按钮有一个 默认的 behavior 来检测Snackbar的添加并让按钮在Snackbar之上呈现上移与Snackbar等高的动画。
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<android.support.v4.widget.NestedScrollView
android:id="@+id/nsv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="CoordinatorLayout \n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n NestedScrollView"
android:textSize="30sp"/>
</android.support.v4.widget.NestedScrollView>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
app:backgroundTint="#00f"
app:layout_anchor="@id/nsv"
app:layout_anchorGravity="bottom|right|end"
app:srcCompat="@android:drawable/ic_dialog_email"/>
</android.support.design.widget.CoordinatorLayout>
显示与隐藏动画
FloatingActionButton的显示和隐藏有点让人困惑,可以找到多种实现方式,而且兼容包里的FloatingActionButton还不断的变化。
基本来说,如果是使用官方的FloatingActionButton,列表滚动时的显示与隐藏都是使用自定义
FloatingActionButton.Behavior来实现的。
NestedScrollView默认
的
app:layout_behavior为:
app:layout_behavior="@string/appbar_scrolling_view_behavior"
<string name="appbar_scrolling_view_behavior" translatable="false">android.support.design.widget.AppBarLayout$ScrollingViewBehavior</string>
动画模板
/**
* Fab在滚动时候的显示与隐藏,直接借用了FloatingActionButton内置的动画效果,仅仅是在条件恰当的时候调用hide()和show()方法
*/
public class ScrollAwareFABBehaviorDefault extends FloatingActionButton.Behavior {
public ScrollAwareFABBehaviorDefault(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child,
View directTargetChild, View target, int nestedScrollAxes) {
// Ensure we react to vertical scrolling
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {// User scrolled down and the FAB is currently visible
child.hide();//如果要自定义动画效果,就在这里重新定义隐藏时的动画
Snackbar.make(coordinatorLayout, "隐藏", Snackbar.LENGTH_SHORT).show();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {// User scrolled up and the FAB is currently not visible
child.show();
Snackbar.make(coordinatorLayout, "显示", Snackbar.LENGTH_SHORT).show();
}
}
}
自定义动画效果1
/**
* Fab在滚动时候的显示与隐藏,自定义上下滑动的动画
*/
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
private boolean mIsAnimatingOut = false;
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child,
View directTargetChild, View target, int nestedScrollAxes) {
// Ensure we react to vertical scrolling
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL
|| super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 && child.getVisibility() == View.VISIBLE) {
child.animate().scaleX(0.0F).scaleY(0.0F).alpha(0.0F).setDuration(1500).start();
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
child.animate().scaleX(1.0F).scaleY(1.0F).alpha(1.0F).setDuration(1500).start();
}
}
}
自定义动画效果2
/**
* Fab在滚动时候的显示与隐藏,自定义上下滑动的动画
*/
public class ScrollAwareFABBehavior extends FloatingActionButton.Behavior {
private int toolbarHeight;
public ScrollAwareFABBehavior(Context context, AttributeSet attrs) {
super();
this.toolbarHeight = getToolbarHeight(context);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
return super.layoutDependsOn(parent, fab, dependency) || (dependency instanceof AppBarLayout);
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton fab, View dependency) {
boolean returnValue = super.onDependentViewChanged(parent, fab, dependency);
if (dependency instanceof AppBarLayout) {
CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) fab.getLayoutParams();
int fabBottomMargin = lp.bottomMargin;
int distanceToScroll = fab.getHeight() + fabBottomMargin;
float ratio = dependency.getY() / toolbarHeight;
fab.setTranslationY(-distanceToScroll * ratio);
}
return returnValue;
}
public static int getToolbarHeight(Context context) {
TypedArray styledAttributes = context.getTheme().obtainStyledAttributes(new int[]{R.attr.actionBarSize});
int toolbarHeight = (int) styledAttributes.getDimension(0, 0);
styledAttributes.recycle();
return toolbarHeight;
}
}
2017-5-23