CoordinatorLayout的使用(二)——自定義Behavior
我們在上一篇文章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
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的原始碼入手,看下整個呼叫流程是怎麼樣的。