如果不了解Android嵌套滚动,最好看一下我之前的文章 Android 嵌套滑动——NestedScrolling完全解析,当然不了解对本篇文章的阅读也不会有太大的阻塞。
第一个简单的自定义Behavior
在Android 5.0 的时候推出了CoordinatorLayout
控件,该控件从翻译上来说称之为 协调性布局,我的理解是,对于他下面的子控件的布局,大小,滚动等等一系列的东西,由每一个子控件商量沟通着来,及每一个控件对于测量,布局等都有发言权,不在是由类似与线性布局一样,你必须垂直着排放,某某必须在下面。那么如何实现每个子控件都有发言权呢,便是通过一个叫behavior
的来进行操作。每一个子控件都可以指定一个behavior
,该behavior
中会包含一些CoordinatorLayout
在measeure
layout
时的回调方法,这样我们可以根据每个控件的不同来进行实现,这样就达到了布局和测量交个子控件自己来做。
首先看一下我们利用behavior
所实现的一个简单效果。
有两个小方块,垂直居中排列,下面的小方块会跟随上面的小方块一起滑动。
该效果可以有多种方式的实现,但为了表述behavior
的原理,实现方式可能会稍显复杂。但目的是为了更好的学习behavior
。
实现分析
因为我们要学习使用behavior
,那么肯定跟布局要使用CoordinatorLayout
,他仅支持layout_gravity
而不支持线性布局的垂直等排列,所以第一个问题便是怎么让下面的小方块放到合适的位置。
其次,上面方块需要能够消耗手指触摸以让自身根据手指的滑动而移动。
最后,便是使下面的方块监听上面方块的移动,以达到跟随上面方块的移动。
根据实现分析,我们来实现代码。
代码实现
首先布局文件activity_behavior.xml
<?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">
<View
android:id="@+id/header"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="50dp"
android:background="#ff00ff"
app:layout_behavior=".behavior.HeaderBehavior" />
<View
android:id="@+id/bottom"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="50dp"
android:background="#33f0ff"
app:layout_behavior=".behavior.BottomBehavior" />
</android.support.design.widget.CoordinatorLayout>
该布局文件中主要定义了两个带有颜色的小方块,其中需要关注三个点:
- 上面小方块定义了id为
header
,注意此id在后面会用到,因为我们下面的小方块是通过第一个方块来定位的。 - 下面小方块中有一个
layout_marginTop
属性,如果我们不做任何操作,那么该小方块会和第一个小方块重合,我们需要的是该margin 是基于第一个小方块的,类似于垂直线性布局的margin。 - 这两个子控件都分别添加了
layout_behavior
属性,该属性的值便是我们自定义的Behvior
,注意代码中省略了包名(类似于activity的注册时可以省略包名一样)。
因为我们所有的操作都是在behavor
中完成的,所以不在叙述Activity
的代码。
从布局中我们可以看到定义了两个behavior
,分别是HeaderBehavior
和BottomBehavior
方法,根据之前的实现分析,他们的分工如下:
HeaderBehavior
:实现第一个小方块消耗手指触摸事件,让自己跟随手指移动。BottomBehavior
:根据第一个小方块的位置,定位自己的位置,在第一个小方块之下;监听第一个小方块,跟随第一个小方块移动。
基于此,我们看一下HeaderBehavior
:
public class HeaderBehavior extends CoordinatorLayout.Behavior<View> {
// 记录手指触摸的位置
private int mLastY = 0;
public HeaderBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onTouchEvent(CoordinatorLayout parent, View child, MotionEvent ev) {
// 手指触摸的回调
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mLastY = (int) ev.getRawY();
break;
case MotionEvent.ACTION_MOVE:
int y = (int) ev.getRawY();
child.setTranslationY(child.getTranslationY() + y - mLastY);
mLastY = y;
break;
}
return true;
}
}
首先,该类继承CoordinatorLayout.Behavior
并且声明了一个泛型,该泛型主要约束当前behavior
可以声明到哪些控件上。如果定义的是RelativeLayout
,则只能声明到RelativeLay