为FloatingActionButton添加滑动隐藏和显示的行为
位于列表上的FloatingActionButton有时会挡住列表的内容,可以通过为FloatingActionButton添加自定义layout_behavior的方式,在向下滚动列表时隐藏FloatingActionButton,向上滚动列表时显示FloatingActionButton,如图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_HORIZONTAL或SCROLL_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()等方法,详情可以参考官方文档。