我们在上一篇文章CoordinatorLayout的使用(一)——简单使用中介绍了CoordinatorLayout的基本用法。为什么CoordinatorLayout能够这么方便的帮助我们非常简单的就实现炫酷的UI交互效果呢?这就不得不提到它的内部类Behavior了。其实CoordinatorLayout本身并没有做太多的事情,就是充当一个触摸事件桥梁的作用,所有的核心实现都是交给Behavior去做的。而我们之前文章使用的AppBarLayout和就是在内部默认使用了
AppBarLayout.Behavior
实现了交互逻辑。
既然Behavior这么重要,所以本篇,我们就介绍一下Behavior,简单实现两个自定义的Behavior。
一、类介绍
这里我们先看下Behavior这个类:
public static abstract class Behavior<V extends View> {
public Behavior() {
}
public Behavior(Context context, AttributeSet attrs) {
}
// 将Behavior设置个LayoutParams的时候调用
public void onAttachedToLayoutParams(@NonNull CoordinatorLayout.LayoutParams params) {
}
// 从LayoutParams移除的时候对调
public void onDetachedFromLayoutParams() {
}
// 这个是在有触摸事件产生的时候,由CoordinatorLayout分发过来。由我们自己决定是否拦截。
// 类似ViewGroup的onInterceptTouchEvent()方法。
/* @param parent 分发此次事件的CoordinatorLayout
* @param child 和该Behavior关联的View
* @param ev the 触摸事件
* @return true:表示要拦截事件,就将后续事件分发给onTouchEvent方法进行处理,fasle表示不进行拦截,默认返回false
*/
public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return false;
}
// 类似于View的onTouchEvent()方法,可以在里面具体处理触摸事件的逻辑。
/* @param parent 分发此次事件的CoordinatorLayout
* @param child 和该Behavior关联的View
* @param ev the 触摸事件
* @return true表示自己消费掉了事件,就不会往后传递事件了。fasle表示自己不消费事件,默认返回false
*/
public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent ev) {
return false;
}
/**
* 给和当前Behavior关联的View区域之外的蒙层,相当于是突出当前的View
* 默认是Black
*/
@ColorInt
public int getScrimColor(CoordinatorLayout parent, V child) {
return Color.BLACK;
}
/**
* 用于指定上面设置蒙层颜色的透明度
* 默认是0.0f
*/
@FloatRange(from = 0, to = 1)
public float getScrimOpacity(CoordinatorLayout parent, V child) {
return 0.f;
}
/**
* 是否阻止交互位于该Behavior绑定View下方View的交互
* 默认是根据这个判断getScrimOpacity(parent, child) > 0.f
*/
public boolean blocksInteractionBelow(CoordinatorLayout parent, V child) {
return getScrimOpacity(parent, child) > 0.f;
}
/**
* 指定当前的View(child)是否要依赖另外一个View(dependency)的位置、大小等的变化而进行调整
* @param parent
* @param child 当前和Behavior绑定的View
* @param dependency 需要依赖关联的View
* @return 如果需要关联,返回true,否则返回fasle
*/
public boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency) {
return false;
}
/**
* 在layoutDependsOn()方法产生关联(返回true)后,dependency的大小、位置等属性有变化,就会回调该方法。我们可以在这里进行相应的处理。比如跟随dependency上移而上移。
* @param parent
* @param child
* @param dependency 所依赖的View
* @return 如果child做出了相应的改变,返回true,否则返回false
*/
public boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency) {
return false;
}
/**
* 所依赖的View被移除了当前的视图数,会接收到该回调。
* @param parent
* @param child
* @param dependency 所依赖的View
*/
public void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency) {
}
/**
* CoordinatorLayout在测量Child的时候,会调用该方法。你可以在该方法里面完成自己的测量逻辑
* @param parent
* @param child
* @param parentWidthMeasureSpec
* @param widthUsed 已经被使用里的宽度
* @param parentHeightMeasureSpec
* @param heightUsed 已经被使用了的高度
* @return 如果自己完成了测量逻辑返回true,CoordinatorLayout就不会再自己对该child进行测量,否则返回false
*/
public boolean onMeasureChild(CoordinatorLayout parent, V child,
int parentWidthMeasureSpec, int widthUsed,
int parentHeightMeasureSpec, int heightUsed) {
return false;
}
/**
* CoordinatorLayout在对子View进行layout的时候会回调该方法。
* @param parent
* @param child
* @param layoutDirection 布局的方向ViewCompat#LAYOUT_DIRECTION_LTR或者ViewCompat#LAYOUT_DIRECTION_RTL
* @return 如果你自己完成了布局,返回true,CoordinatorLayout不会再对该child进行布局,否则返回false
*/
public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
return false;
}
/**
* 设置标志,和View.setTag作用一样,我们可以在里面保存一个我们需要的对象
* @param child child view to set tag with
* @param tag tag object to set
*/
public static void setTag(View child, Object tag) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
lp.mBehaviorTag = tag;
}
/**
* 获得我们设置的标志对象
* @param child child view to get tag with
* @return the previously stored tag object
*/
public static Object getTag(View child) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
return lp.mBehaviorTag;
}
/**
* 如果CoordinatorLayout有可以可支持的嵌套滑动View(如NestedScrollView等),在NestedScrollView触发滑动后,但是还没有对手指滑动距离进行处理前,会先回调该方法。
* @param coordinatorLayout
* @param child
* @param directTargetChild 包含NestedScrollView的CoordinatorLayout的直接子View
* @param target 真正触发滑动的View
* @param nestedScrollAxes 滑动方向ViewCompat#SCROLL_AXIS_HORIZONTAL或者ViewCompat#SCROLL_AXIS_VERTICAL}
* @return 如果我们想要自己处理滑动,返回true,否则返回false。返回false后,后面有关嵌套滑动的几个方法就不会被调用了。
*/
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
V child, View directTargetChild, View target, int nestedScrollAxes) {
return false;
}
// onStartNestedScroll()放回true后,会紧接着被调用,我么可以做一些滑动的准备工作。
// 参数同上
public void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child,
View directTargetChild, View target, int nestedScrollAxes) {
// Do nothing
}
// 本次嵌套滑动停止的时候(是指用户停止滑动,不是指NestedScrollView停止滚动,因为有惯性的因素,后续还会继续滚动)
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
// Do nothing
}
/**
如果onStartNestedScroll()返回true,系统会将本次用户滑动的距离传过来,可以做优先处理。
* @param coordinatorLayout
* @param child the
* @param target t
* @param dx 水平方向滑动的距离
* @param dy 垂直方向滑动的距离
* @param consumed 传出参数,用于记录我们自己消费掉的参数consumed[0]记录我们水平方向消费的距离,consumed[1]记录垂直方向我们消费的距离
* @see NestedScrollingParent#onNestedPreScroll(View, int, int, int[])
*/
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
int dx, int dy, int[] consumed) {
}
/**
* 在onNestedPreScroll()调用后,NestedScrollView会根据我们消费的距离,自己再做处理,然后再调用该方法,通知我们是否还有未消费完的距离。
* @param coordinatorLayout
* @param child
* @param target
* @param dxConsumed 被NestedScrollView消费的水平距离
* @param dyConsumed 被NestedScrollView消费的垂直距离
* @param dxUnconsumed 未被NestedScrollView消费的水平距离
* @param dyUnconsumed 未被NestedScrollView消费的垂直距离
*/
public void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) {
// Do nothing
}
/**
* 嵌套滑动中,惯性事件的处理
* @param coordinatorLayout
* @param child
* @param target
* @param velocityX 水平方向的速度
* @param velocityY 垂直方向的速度
* @param consumed true NestedScrollView是否消费了惯性事件
* @return 如果我们消费了事件,返回true
*
* @see NestedScrollingParent#onNestedFling(View, float, float, boolean)
*/
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target,
float velocityX, float velocityY, boolean consumed) {
return false;
}
/**
* CoordinatorLayout的子View里面如果有支持嵌套滑动的,在嵌套滑动过程中的Fling开始的时候会首先回调该方法,在里面处理Fling事件。并通过返回值告诉CoordinatorLayout自己是否处理了
* @param coordinatorLayout
* @param child
* @param target CoordinatorLayout的子View里面支持嵌套查询的那个View。也就是触发本次嵌套滑动的View
* @param velocityX 水平方向的速度
* @param velocityY 垂直方向的速度
* @return 如果自己消费了Fling事件,返回true,否则返回fasle
*/
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
float velocityX, float velocityY) {
return false;
}
// 如果给CoordinatorLayout设置了fitSystemWindow=true,可以在这里自己处理WindowInsetsCompat
@NonNull
public WindowInsetsCompat onApplyWindowInsets(CoordinatorLayout coordinatorLayout,
V child, WindowInsetsCompat insets) {
return insets;
}
// 在CoordinatorLayout的requestChildRectangleOnScreen()中被调用
public boolean onRequestChildRectangleOnScreen(CoordinatorLayout coordinatorLayout,
V child, Rect rectangle, boolean immediate) {
return false;
}
/**
* 恢复之前保存的状态
*/
public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
// no-op
}
/**
* 保存状态
*/
public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
return BaseSavedState.EMPTY_STATE;
}
// 处理遮挡覆盖的问题,rect是一个传出参数,需要我们把调整好的位置记录在里面
/**
* @param parent
* @param child
* @param rect 记录调整后的位置
* @return true:说明我们进行位置调整,fasle:我们没有调整位置
*/
public boolean getInsetDodgeRect(@NonNull CoordinatorLayout parent, @NonNull V child,
@NonNull Rect rect) {
return false;
}
}
Behavior
里面的方法,在注释里面都写的比较清楚了。方法还不少,里面有些方法平时用的不多,后面就没有进行示例介绍,感兴趣可以自己研究研究。平时我们使用的时候,主要就是用在两个方面。
1、一个View跟随另外一个View的变化而变化
2、嵌套滑动的交互。
根据这两个用途的不同,我们所需要关注的方法也不同,下面我们就从这两个方面进行自定义Behavior的介绍。
二、自定义Behavior
这里在具体介绍示例之前,先大致说下自定义Behavior的流程,很简单就两步
首先、自定义类继承自Behavior,然后选择需要重写的方法进行重写实现。
然后,将Behavior绑定到的指定的View上。绑定也有两种方式,a)在xml布局文件中,通过
app:layout_behavior
属性,设置好我们自定义Behavior的类全名。b)在代码里面,通过
LayoutParams
的
setBehavior(@Nullable Behavior behavior)
方式绑定。
知道流程后,我们就开始撸代码吧。
1、产生依赖关系的使用
先放一个我们需要实现效果图
可以看到这里的HelloWorld的View随着我们向上滑动展示出来了,向下滑动隐藏了。
那我们就看具体的实现吧,在这种使用情景下,我们需要重点关注一下几个方法:
boolean layoutDependsOn(CoordinatorLayout parent, V child, View dependency)
boolean onDependentViewChanged(CoordinatorLayout parent, V child, View dependency)
void onDependentViewRemoved(CoordinatorLayout parent, V child, View dependency)
根据上面的自定义Behavior的步骤,先创建Behavior类。如下:
/**
* @author Created by victor on 2018/12/11.
* @since Version
*/
public class DependencyBehavior extends CoordinatorLayout.Behavior<View> {
private float deltaY;
public DependencyBehavior() {
}
public DependencyBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 这里的child就是我们上面中HelloWord所在的View咯
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
// 这里表示 我们需要依赖RecyclerView
boolean isDependency = dependency instanceof RecyclerView;
if (isDependency) {
RecyclerView recyclerView = (RecyclerView) dependency;
}
return isDependency;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
// 获取到RecyclerView的Y坐标
float dependencyY = dependency.getY();
if (deltaY == 0) {
// 第一次先获取到初始状态下RecyclerView的Y坐标和绑定的View的高度差值作为后面计算的基值
deltaY = dependencyY - child.getHeight();
}
// 根据RecyclerView移动,计算当前的差值
float dy = dependencyY - child.getHeight();
dy = dy < 0 ? 0 : dy;
// 求出当前需要移动的距离
float y = -(dy / deltaY) * child.getHeight();
float preTranslationY = child.getTranslationY();
if (y != preTranslationY) {
// 移动HelloWorld 并返回true
child.setTranslationY(y);
return true;
}
return false;
}
}
实现很简单,在这个
layoutDependsOn()
方法里面,我们告诉系统需要依赖RecyclerView。然后我们滑动RecyclerView的时候,就会回调到
onDependentViewChanged()
这个方法里面。然后根据当前滑动的距离,通过
setTranslationY()
来控制被绑定View的显示和隐藏就可以了。
这里有个注意点:
我们在自定义Behavior的时候,如果要在xml中使用的话,一定要有两个参数的构造方法,否则就会报如下错误
Caused by: java.lang.RuntimeException: Could not inflate Behavior subclass com.victor.coordinatorlayoutdemo.behavior.DependencyBehavior
at android.support.design.widget.CoordinatorLayout.parseBehavior(CoordinatorLayout.java:615)
……
Caused by: java.lang.NoSuchMethodException: <init> [class android.content.Context, interface android.util.AttributeSet]
at java.lang.Class.getConstructor0(Class.java:2204)
at java.lang.Class.getConstructor(Class.java:1683)
这里
onDependentViewRemoved()
方法我么没有重写处理,这个例子中暂时没有看到有需要用到的地方。感兴趣的可以自己去试试。
接下来,我们的布局文件:
<?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.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="#00ffffff"
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<ImageView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="@mipmap/ctl_bg"
android:fitsSystemWindows="true"
android:scaleType="fitXY"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.7"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#ff0000"
android:gravity="center"
android:text="Hello World"
android:textColor="#ffffff"
android:textSize="18sp"
app:layout_behavior="@string/dependency_behavior"/>
<!-- 这里设置给TextView上 -->
</android.support.design.widget.CoordinatorLayout>
这里我们也参考Google官方的做法,将类全类名放到string.xml资源文件中
<resources>
<string name="app_name">CoordinatorLayoutDemo</string>
<string name="dependency_behavior">com.victor.coordinatorlayoutdemo.behavior.DependencyBehavior</string>
</resources>
这样,一个自定义Behavior步骤就完成了,后面就是在代码里面添加模拟数据了,是不是很简单呢。
public class CustomerBehaviorActivity extends AppCompatActivity {
List<String> mDatas = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_customer_befavior);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
for (int i = 0; i < 50; i++) {
mDatas.add("Item " + i);
}
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(new RecyclerView.Adapter<CustomerBehaviorActivity.MyViewHolder>() {
@Override
public CustomerBehaviorActivity.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView textView = new TextView(CustomerBehaviorActivity.this);
textView.setPadding(0,20, 0, 20);
return new MyViewHolder(textView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(mDatas.get(position));
}
@Override
public int getItemCount() {
return mDatas.size();
}
});
}
class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public MyViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView;
}
}
}
跑起来之后,就能看到上面的效果了。接下来,我们接着说第二种使用情景,嵌套滑动。
2、嵌套滑动的使用
这种使用情景下,我们主要是关心下面的方法:
boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout,
V child, View directTargetChild, View target, int nestedScrollAxes)
void onNestedScrollAccepted(CoordinatorLayout coordinatorLayout, V child,
View directTargetChild, View target, int nestedScrollAxes)
void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target)
void onNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target,
int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed)
void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target,
int dx, int dy, int[] consumed)
boolean onNestedFling(CoordinatorLayout coordinatorLayout, V child, View target,
float velocityX, float velocityY, boolean consumed)
boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
float velocityX, float velocityY)
方法介绍还是看上面类介绍的方法注释吧。这里我们还是先看下要实现的效果吧
这里我们滑动RecyclerView列表的时候,顶部的HelloWorld也跟着上下滑动了。
还是先来自定义个Behavior吧
public class SampleHeaderBehavior extends CoordinatorLayout.Behavior<TextView> {
private int mOffsetTopAndBottom;
private int mLayoutTop;
public SampleHeaderBehavior() {
}
public SampleHeaderBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
// 这个方法里,我们并没有自己布局,还是直接通过parent去布局,重写该方法只是为了获取初始top值
@Override
public boolean onLayoutChild(CoordinatorLayout parent, TextView child, int layoutDirection) {
parent.onLayoutChild(child, layoutDirection);
// 获取到child初始的top值
mLayoutTop = child.getTop();
return true;
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, TextView child, View directTargetChild, View target, int nestedScrollAxes) {
// 这里我们只关系垂直方向的滚动
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, TextView child, View target, int dx, int dy, int[] consumed) {
if (dy != 0 ) {
// 如果本次滑动距离不为0,进行自己的滚动操作
consumed[1] = scroll(child, dy);
}
}
// 获取childView最大可滑动距离
private int getChildScrollRang(View childView) {
if (childView == null) {
return 0;
}
return childView.getHeight();
}
// 滚动child
private int scroll(View child, int dy) {
int consumed = 0; // 记录我们消费的距离
int offset = mOffsetTopAndBottom - dy; // 计算出本次需要滚动到的位置
int minOffset = -getChildScrollRang(child);
int maxOffset = 0;
// 调整滚动距离,在0和最大可滑动距离的负数之间(因为是向上滑动,所以是负数哦)
offset = offset < minOffset ? minOffset : (offset > maxOffset ? maxOffset : offset);
// 通过offsetTopAndBottom()进行滚动
ViewCompat.offsetTopAndBottom(child, offset - (child.getTop() - mLayoutTop));
// 计算消费的距离
consumed = mOffsetTopAndBottom - offset;
// 将本次滚动到的位置记录下来
mOffsetTopAndBottom = offset;
return consumed;
}
}
这里只是做个展示举例,所以实现也很简单,代码里面注释也比较清楚了,就不再讲解了。
不过这里并没有处理Fling事件哦,如果我们快速滑动,产生Fling的时候,我们的HelloWord是不会滚动的。有兴趣的可以自己通过OverScroller去实现Fling的逻辑。
由于这里我们把滚动事件交给我们自己的Behavior消费处理了。那RecyclerView就没法消费滑动距离,也就不会产生滚动了,所以这里我们还需要多处理一步,手动移动RecyclerView,这里也就是我们上面第一种情景下的使用方式了,所以我们再新建一个自定义Behavior
public class ScrollerBehavior extends CoordinatorLayout.Behavior<RecyclerView> {
public ScrollerBehavior() {
}
public ScrollerBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean layoutDependsOn(CoordinatorLayout parent, RecyclerView child, View dependency) {
// 依赖TextView(也就是上面HellorWorld所在的View)
return dependency instanceof TextView;
}
@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
// 如果我们所依赖的View有变化,也是通过offsetTopAndBottom移动我们的RecyclerView
ViewCompat.offsetTopAndBottom(child, (dependency.getBottom() - child.getTop()));
return false;
}
}
类写完了,使用起来吧,还是将上面两个Behavior的全类名定义到string.xml中
<resources>
<string name="app_name">CoordinatorLayoutDemo</string>
<string name="dependency_behavior">com.victor.coordinatorlayoutdemo.behavior.DependencyBehavior</string>
<string name="behavior_sample_header">com.victor.coordinatorlayoutdemo.behavior.SampleHeaderBehavior</string>
<string name="behavior_recyclerview">com.victor.coordinatorlayoutdemo.behavior.ScrollerBehavior</string>
</resources>
然后再布局文件中使用:
<?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">
<TextView
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#ff0000"
android:gravity="center"
android:text="Hello World"
android:textColor="#ffffff"
android:textSize="18sp"
app:layout_behavior="@string/behavior_sample_header" />
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_nested_scrolling"
android:layout_width="match_parent"
app:layout_behavior="@string/behavior_recyclerview"
android:layout_height="wrap_content" />
</android.support.design.widget.CoordinatorLayout>
最后再代码里面模拟一组数据给RecyclerView
public class CustomerBehaviorNestedScrollActivity extends AppCompatActivity {
List<String> mDatas = new ArrayList<>();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_nested_scrolling);
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv_nested_scrolling);
for (int i = 0; i < 50; i++) {
mDatas.add("Item " + i);
}
recyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
recyclerView.setAdapter(new RecyclerView.Adapter<MyViewHolder>() {
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
TextView textView = new TextView(CustomerBehaviorNestedScrollActivity.this);
textView.setPadding(0, 20, 0, 20);
return new MyViewHolder(textView);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.mTextView.setText(mDatas.get(position));
}
@Override
public int getItemCount() {
return mDatas.size();
}
});
}
class MyViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public MyViewHolder(View itemView) {
super(itemView);
mTextView = (TextView) itemView;
}
}
}
这样就完成了我们第二种情景的自定义Behavior。相对于第一种使用方式,此种使用稍微复杂一定。
通过上面的两个例子,我们发现,起始自定义
Behavior
并不复杂,复杂的是要理解其中的调用逻辑。如:每个方法是怎么和CoordinatorLayout配合的,是什么时候被调用的等等。只要我们搞明白调用逻辑后,我们就能根据实际情况,选择我们需要实现的方法,做出相应的逻辑处理,到时我们自己也能实现类似AppBarLayout这种复杂的炫酷的交互逻辑了。所以我们下一篇文章就需要从CoordinatorLayout的源码入手,看下整个调用流程是怎么样的。