我們在上一篇文章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的源碼入手,看下整個調用流程是怎麼樣的。