简介
CoordinatorLayout,中文应译:协调者布局。见名知意,CoordinatorLayout 主要用途有两个:
- 作为顶层布局使用。
- 作为一个容器,与一个或者多个子
View
进行特定交互。
来看下 CoordinatorLayout 的文档:
CoordinatorLayout从文档中可以看出,CoordinatorLayout 是在 design 包中,它是一个ViewGroup
,是一个更强力的FrameLayout
。
通过为 CoordinatorLayout 的子View
指定 Behaviors,你可以在单一父View
下提供许多不同的交互,同时也可以让子View
间各自进行交互。
所以,其实 CoordinatorLayout 的核心操作是 Behavior:
那么我们来看下 Behavior 的文档:
Behavior一个 Behavior 可以为用户实现对 CoordinatorLayout 的一个子View
的一个或多个交互。这些交互包括 drags, swipes, flings 以及其他手势操作。
简单理解就是,Behavior 就是定制子View
间的交互行为逻辑。
Behavior 内部持有一个泛型View
(Behavior<V extends android.view.View
),该View
是 CoordinatorLayout 的子View
,并且是要响应其他子View
的事件;也就是说,该View
依赖于其他View
。
让我们从Behavior 比较重要的两个方法来理解上面所讲的内容:
/**
* Determine whether the supplied child view has another specific sibling view as a
* layout dependency.
*
* @param parent the parent view of the given child
* @param child the child view to test
* @param dependency the proposed dependency of child
* @return true if child's layout depends on the proposed dependency's layout,
* false otherwise
*
* @see #onDependentViewChanged(CoordinatorLayout, View, View)
*/
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
return false;
}
/**
* Respond to a change in a child's dependent view
*
* @param parent the parent view of the given child
* @param child the child view to manipulate
* @param dependency the dependent view that changed
* @return true if the Behavior changed the child view's size or position, false otherwise
*/
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
return false;
}
这里首先要理解下上面两个方法中的 child 和 dependency 两个View
。child 就是 CoordinatorLayout 里面的一个子View
,而 dependency 是 child 依赖的View
。当 dependency 行为改变时, child 就会被它设置的Behavior
通知到,从而做出相应的改变(这就完成了 child 和 dependency 之间的交互)。
说到这里,现在你大概就明白了,其实 CoordinatorLayout 的出现就是为了解耦子View
与子View
之间的交互。所以,如果你的应用需要多个View
之间进行交互,不妨考虑下使用 CoordinatorLayout 包裹这些View
,然后为各个子View
引入相应的 Behavior 来实现交互。
示例
那么下面我们就来写一个 Demo 来看下 CoordinatorLayout 如何通过自定义 Behavior 来实现各子View
间的交互把。
要求:
假设当前布局中有一个RecyclerView
和一个FloatingActionButton
,要求当RecyclerView
滑动时,让FloatingActionButton
隐藏;当RecyclerView
停止滑动时,让FloatingActionButton
显示。
分析:
由于要求的时RecyclerView
滑动时,FloatingActionButton
进行响应,也就是说FloatingActionButton
的行为依赖于RecyclerView
,因此,可以让 CoordinatorLayout 包裹这两个控件,然后给FloatingActionButton
添加一个响应RecyclerView
滑动事件的自定义 Behavior,让FloatingActionButton
可以响应RecyclerView
的滑动事件。
代码详情
- 首先先为
FloatingActionButton
自定义一个 Behavior:
package com.yn.demos.masterial_design.CoordinatorLayout;
import android.content.Context;
import android.graphics.CornerPathEffect;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
public class FabVisibilityBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {
// important !!
public FabVisibilityBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
Log.d("Why8n", "onStartNestedScroll");
if (axes == ViewCompat.SCROLL_AXIS_VERTICAL) { //scroll vertical
child.hide();
return true;
}
return super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
}
@Override
public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull FloatingActionButton child, @NonNull View target, int type) {
super.onStopNestedScroll(coordinatorLayout, child, target, type);
Log.d("Why8n", "onStopNestedScroll");
if (!child.isShown())
child.show();
}
}
- 布局如下所示:
<?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:orientation="vertical">
<android.support.v7.widget.RecyclerView
android:id="@+id/rc"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<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="20dp"
android:src="@drawable/emoticon"
app:borderWidth="0dp"
app:elevation="50dp"
app:fabSize="normal"
app:layout_behavior="com.yn.demos.masterial_design.CoordinatorLayout.FabVisibilityBehavior"
app:rippleColor="#e7d16b" />
</android.support.design.widget.CoordinatorLayout>
- 效果如下:
代码解析
上面代码中,主要时覆写了 Behavior 的两个方法:
方法 | 解释 |
---|---|
onStartNestedScroll |
当 CoordinatorLayout 的直接或者非直接子View开始准备嵌套滑动的时候会调用; 如果 Behavior 对滑动事件感兴趣,该方法必须返回 true ,这样后续的滑动事件才能被接收
|
onNestedScroll |
当目标对象正在滑动或者将要滑动时被调用 |
onStopNestedScroll |
当嵌套滑动停止时被调用 |
所以上面代码中,我们在自定义的 FabVisibilityBehavior
覆写了onStartNestedScroll
和onStopNestedScroll
方法,并让onStartNestedScroll
返回true
,这样onStopNestedScroll
才会被我们自定义的 FabVisibilityBehavior
接收到,从而在这两个方法中对FloatingActionButton
进行显示和隐藏。
最后在布局文件中通过app:layout_behavior="com.yn.demos.masterial_design.CoordinatorLayout.FabVisibilityBehavior"
将我们自定义的 Behavior 添加到FloatingActionButton
中,这样当 CoordinatorLayout 发生滑动事件时,FloatingActionButton
借助于该自定义的FabVisibilityBehavior
就可以进行响应了。
注:由于我们是在 xml 中静态的导入自定义 Behavior,为了让 layout inflation 顺利进行,我们必须实现一个构造函数:
public FabVisibilityBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
否则,将会出现 “Could not inflate Behavior subclass” 的错误信息。
网友评论