为FloatingActionButton添加滑动隐藏和显示的行为

  位于列表上的FloatingActionButton有时会挡住列表的内容,可以通过为FloatingActionButton添加自定义layout_behavior的方式,在向下滚动列表时隐藏FloatingActionButton,向上滚动列表时显示FloatingActionButton,如图1所示。

图1

图1

0. 布局

  这里使用的布局如下,在CoordinatorLayout中放置了RecyclerView和FloatingActionButton:

<?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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    ...
    
    <android.support.v7.widget.RecyclerView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layoutManager="LinearLayoutManager"/>

    <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"
        android:src="@android:drawable/ic_dialog_email"/>

</android.support.design.widget.CoordinatorLayout>

完整文件

1. 继承FloatingActionButton.Behavior实现自定义行为

  首先需要继承FloatingActionButton.Behavior并实现自定义的行为,FloatingActionButton.Behavior继承自CoordinatorLayout.Behavior,这里需要重写onStartNestedScroll()onNestedScroll()

public class ScrollAwareFabBehavior extends FloatingActionButton.Behavior {

    public ScrollAwareFabBehavior(Context context, AttributeSet attrs) {
        super();
    }

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
                                       FloatingActionButton child, View directTargetChild,
                                       View target, int nestedScrollAxes) {
        return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL;
    }

    @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.hide();
        } else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
            child.show();
        }
    }
}

完整文件

  当CoordinatorLayout中的成员进行嵌套滑动(Nested Scroll)时,Behavior的onStartNestedScroll()就会被调用,只有当onStartNestedScroll()返回true时,Behavior才能够接收到后续的Nested Scroll事件。onStartNestedScroll()的参数nestedScrollAxes指示了滑动的轴线方向,这里在垂直滑动时返回true,只接收垂直滑动的事件。

  当滑动事件发生时,onNestedScroll()就会被调用,参数dxConsumed和dyConsumed为实际滑动的水平和垂直像素数,这里根据dyConsumed判断滑动方向,控制FloatingActionButton的隐藏和显示。

2. 配置layout_behavior

  然后要把ScrollAwareFabBehavior配置到FloatingActionButton,只需配置layout_behavior为ScrollAwareFabBehavior即可。

<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"
    android:src="@android:drawable/ic_dialog_email"
    app:layout_behavior="com.nex3z.examples.floatingactionbuttonbehavior.ScrollAwareFabBehavior"/>

完整文件

3. 完整代码

  完整代码可以在这里找到。

4. 更多

  上面使用的onStartNestedScroll()有很多参数,其完整签名为:

boolean onStartNestedScroll (CoordinatorLayout coordinatorLayout, 
                V child, 
                View directTargetChild, 
                View target, 
                int nestedScrollAxes)

其中各参数的含义为:

  • coordinatorLayout:与Behavior所关联的父CoordinatorLayout。在上面的例子中,就是布局文件顶部的CoordinatorLayout。
  • child:与Behavior关联的CoordinatorLayout中的子View。在上面的例子中,就是FloatingActionButton。
  • directTargetChild:滑动事件的目标View,或者目标View的容器,他们也都是CoordinatorLayout中的子View。在上面的例子中,就是RecyclerView。
  • target:发起滑动的CoordinatorLayout的子View,在上面的例子中,也是RecyclerView。
  • nestedScrollAxes:滑动的方向,可以使SCROLL_AXIS_HORIZONTALSCROLL_AXIS_VERTICAL

  onNestedScroll()的完整签名为:

void onNestedScroll (CoordinatorLayout coordinatorLayout, 
                V child, 
                View target, 
                int dxConsumed, 
                int dyConsumed, 
                int dxUnconsumed, 
                int dyUnconsumed)

其中各参数的含义为:

  • coordinatorLayout:与Behavior所关联的父CoordinatorLayout。在上面的例子中,就是布局文件顶部的CoordinatorLayout。
  • child:与Behavior关联的CoordinatorLayout中的子View。在上面的例子中,就是FloatingActionButton。
  • target:发起滑动的CoordinatorLayout的子View,在上面的例子中,就是RecyclerView。
  • dxConsumed,dyConsumed:target所滑动的水平和垂直距离(单位是像素)。
  • dxUnconsumed,dyUnconsumed:用户进行了滑动操作,但实际上target并没有进行滑动的水平和垂直距离(单位是像素),如滑动到了列表顶部/底部的情况。

  除了上面用到的onStartNestedScroll()和onNestedScroll()之外,CoordinatorLayout.Behavior还有onStopNestedScroll()、onNestedFling()等方法,详情可以参考官方文档