给 CoordinatorLayout 自定义 Behavior
文章目录
在Design Support Library中,Google为我们提供了一系列实现Material Design的控件:TextInputLayout、NavigationView、FloatingActionButton等。
CoordinatorLayout,为我们处理触摸事件提供了一种新的方式。在CoordinatorLayout的使用中,最为核心的就是CoordinatorLayout.Behavior这个内部类了
从CoordinatorLayout开始
CoordinatorLayout is a super-powered FrameLayout. CoordinatorLayout is intended for two primary use cases:
- As a top-level application decor or chrome layout
- As a container for a specific interaction with one or more child views
从开发者文档的描述中我们可以知道,CoordinatorLayout是一个加强版的FrameLayout,他有两个主要作用:
- 作为根布局
- 用于处理一个或多个子控件之间特殊的交互的容器
何为多个子控件之间的交互呢?在之前的博客(ANDROID 触摸事件分发源码)中有提及,在ViewGroup处理触摸事件时,只会在ACTION_DOWN的时候去判断哪一个子控件可以接收处理事件;当某一个子控件可以处理ACTION_DOWN事件时,后续的一系列手势会直接传递到该控件,其他子控件是无法直接响应触摸事件的。
如果我们需要某个子控件监听另一个子控件、或者监听父控件的滚动,并做出响应?通常我们会定义一个接口,通过接口的回调在另一个控件中修改属性。
而现在,可以直接通过CoordinatorLayout对这些控件进行包裹,来处理这些子控件之间的交互
CoordinatorLayout.Behavior
那么,怎样才能在CoordinatorLayout中进行子控件之间的交互呢?这就需要我们来自定义不同的CoordinatorLayout.Behavior了
Behavior是CoordinatorLayout的内部类,在这个类的内部提供了一系列的回调方法:
layoutDependsOn()
:将两个控件绑定起来,child监听dependencyonDependentViewChanged()
:在dependency状态改变时回调onStartNestedScroll ()
:根据返回值来决定是否要处理这次neast scrollonNestedPreScroll()
: 在子控件响应滑动之前回调onNestedScroll()
:子控件响应滑动时回调onNestedPreFling()
|onNestedFling()
:惯性滑动
主要需要重写的就是这些方法,通常
- 如果需要让某个控件监听另一个控件的状态,需要重写
layoutDependsOn()
和onDependentViewChanged()
方法 - 如果需要让某个控件监听父控件的滑动,需要重写另外四个方法中的部分或全部
自定义一个Beahavior
现在我们有这么一个需求,在CoordinatorLayout中有一个RecyclerView和一个FAB,需要在RecyclerView下滑时隐藏FAB、上滑时显示FAB。现在大多数使用Material Design的App都会有这么一个需求
public class ScrollBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
// 由于需要在布局文件中使用,所以必须重写这个构造函数
public ScrollBehavior(Context context, AttributeSet attrs) {
super();
}
@Override
public boolean onStartNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child,
final View directTargetChild, final View target, final int nestedScrollAxes) {
// 对垂直方向的滑动进行处理
return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL ||
super.onStartNestedScroll(coordinatorLayout, child, directTargetChild,
target, nestedScrollAxes);
}
@Override
public void onNestedScroll(final CoordinatorLayout coordinatorLayout, final FloatingActionButton child, final
View target, final int dxConsumed, final int dyConsumed, final int dxUnconsumed, final int dyUnconsumed) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed);
if (dyConsumed > 0 &&
child.getVisibility() == View.VISIBLE) {
// 用户向下滑,并且FAB是VISIBLE时 -> hide the FAB
child.hide();
child.setVisbility(View.GONE);
} else if (dyConsumed < 0 && child.getVisibility() != View.VISIBLE) {
// 用户向上滑,并且FAB当前不是VISIBLE时 -> show the FAB
child.setVisbility(View.VISIBLE);
child.show();
}
}
}
自定义Behavior之后,在布局文件中给FloatingActionButton添加上属性,就可以实现想要的UI了
<android.support.design.widget.CoordinatorLayout
android:id="@+id/main_content"
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.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_margin="16dp"
android:src="@android:drawable/ic_dialog_email"
app:layout_behavior=".ScrollBehavior"/>
</android.support.design.widget.CoordinatorLayout>
要注意的是,在CoordinatorLayout中,使用ListView或者GridView作为传递滑动的控件是不能响应CoordinatorLayout.Behavior的。 似乎是因为ListView和GridView中没有添加相关的代码。使用RecyclerView就可以了
文章作者 Dio.Ye
上次更新 2016-03-02