MaterialDesign系列文章(五)Behavior的使用

原本今天早上起來就想寫這篇文章的,可是看着心愛的騎士隊正在打比賽,想着詹皇的2:0。。。想着猛龍的東部第一,今天真的是給猛龍打成「朦朧」了。。。猛龍的球迷不會打我吧!!!哈哈android

詹皇鎮樓

昨天有小夥伴說要我說一下Behavior的使用,對於這個東西我也不是很瞭解,可是不瞭解能夠學嗎!!!其實做爲程序員,咱們在平常開發中會接觸到不少新東西,不是咱們每個都要去了解的,咱們也不會有那麼多的精力。可是爲何有的人能夠會的那麼多呢?其實有的時候我總在想這個問題,後來我發現一件頗有意思的事情,他們也不見得什麼都懂,可是以往的經歷讓他們知道怎麼去接觸一個新的東西,怎麼去快速上手這個東西。其實咱們應該培養的是解決問題的能力,而不是什麼都會。。。好了,閒話就扯到這裏吧!下面開始今天的內容。其實我挺喜歡詹皇的!哈哈。。。git

本文知識點:

  • Behavior是什麼東東:
  • 已有的Behavior有哪些:
    • BottomSheetBehavior的使用
    • SwipeDismissBehavior的使用
  • Behavior裏面回調的說明
  • 幾個常見的Behavior的案例:

1. Behavior是什麼東東

關於Behavior的描述是這樣的程序員

Interaction behavior plugin for child views of CoordinatorLayout. A Behavior implements one or more interactions that a user can take on a child view. These interactions may include drags, swipes, flings, or any other gestures.github

簡單的翻譯一下:CoordinatorLayout中子View的交互行爲,能夠在CoordinatorLayout的子類中實現一個或多個交互,這些交互多是拖動,滑動,閃動或任何其餘手勢。其實就是實現CoordinatorLayout內部控件的交互行爲,能夠在非侵入的方式實現相應的交互!他能作什麼呢?看了後面就知道了!!!哈哈。。。算法

2. 已有的Behavior有哪些:

關於這個問題,我去google找了找,下面這張圖說明一切:bash

但其實,開發中經常使用的就BottomSheetBehavior、SwipeDismissBehavior剩下的就是自定義了。app

2.1 BottomSheetBehavior的使用

BottomSheetBehavior主要是實現從底部彈出內容的Behavior。其實這個裏面包含不少內容,像BottomSheet、BottomSheetDialog、BottomSheetDialogFragment咱們這裏一個一個說明一下:ide

2.1.1 BottomSheet的使用

這個通常是使用在相應的佈局中的!爲何這麼說呢,由於它能夠直接在佈局中使用,就醬紫啊!!!先介紹一下里面比較重要的概念,不然我怕你嚇啥麼不知道啥麼啥(原諒個人一口東北話)!佈局

效果就是醬紫了

  • app:layout_behavior="@string/bottom_sheet_behavior" 最重要的一句,沒有它都是耍流氓!
  • app:behavior_peekHeight="0dp" 可見的部分高度(我發現若是不設置這個東西,底部的內容會一直在上面不會相應事件,因此若是你不想看見它就設置成0不然隨意)
  • app:behavior_hideable="true" 是否能經過下滑手勢收起
  • static BottomSheetBehavior from(V view) 獲取相應的BottomSheetBehavior對象
  • setBottomSheetCallback(BottomSheetCallback callback) 相應的監聽
    • onStateChanged(@NonNull View bottomSheet, int newState) 狀態改變的回調
    • onSlide(@NonNull View bottomSheet, float slideOffset) 滑動的時候調用
  • getState() 獲取相應的狀態
  • setState(final @State int state) 設置相應的狀態
    • STATE_COLLAPSED: 默認的摺疊狀態
    • STATE_DRAGGING : 過渡狀態
    • STATE_SETTLING: 視圖從脫離手指自由滑動到最終停下的這一小段時間
    • STATE_EXPANDED: bottom sheet 處於徹底展開的狀態
    • STATE_HIDDEN : 默認無此狀態(可經過app:behavior_hideable 啓用此狀態),啓用後用戶將能經過向下滑動徹底隱藏

根佈局是CoordinatorLayout,這個是重點啊!!!ui

這裏注意幾點問題:

  • 獲取BottomSheetBehavior對象的時候,使用的是設置有app:layout_behavior="@string/bottom_sheet_behavior"的佈局
  • app:behavior_peekHeight="0dp"必定要設置,不然你會在界面上一直看到它,並沒有法響應你的手勢

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"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.jinlong.newmaterialdesign.behavior.BehaviorActivity">

    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="bottomSheet"
        android:text="展現bottomSheet" />

    <LinearLayout
        android:id="@+id/ll_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:behavior_peekHeight="50dp"
        app:layout_behavior="@string/bottom_sheet_behavior">

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#f25b41"
            android:gravity="center"
            android:text="底部還有內容啊!!!" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#009988"
            android:gravity="center"
            android:text="標籤1" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#002288"
            android:gravity="center"
            android:text="標籤2" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#009922"
            android:gravity="center"
            android:text="標籤3" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#00aa88"
            android:gravity="center"
            android:text="標籤4" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:background="#999988"
            android:gravity="center"
            android:text="標籤5" />
    </LinearLayout>
</android.support.design.widget.CoordinatorLayout>
複製代碼

主頁面的邏輯

BottomSheetBehavior<LinearLayout> bottomSheetBehavior = BottomSheetBehavior.from(mLlBottomSheet);
        if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) {
            //展開狀態,隱藏
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
        } else {
            //其餘的狀態展開
            bottomSheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED);
        }
複製代碼

2.1.2 BottomSheetDialog的使用

其實這個東西的使用和對話框的使用基本上是同樣的。你setContentView()進去一個佈局,而後調用show()方法展現一下就能夠了,可是這裏有一個特別須要注意的地方,若是你在對話框中設置的佈局超過整個屏幕的話(這裏不是說你設置了match就是全屏了,是有效內容。這裏建議你試試就知道了),整個內容不會鋪滿全屏,頂部會留出一段空間,和peek的效果相似,這裏注意一下就能夠了!其餘的使用和對話框的使用同樣,這裏直接貼一下主要代碼!!!

BottomSheetDialog sheetDialog = new BottomSheetDialog(this);        sheetDialog.setContentView(R.layout.sheet_dialog);
    sheetDialog.show();
複製代碼

2.1.3 BottomSheetDialogFragment的使用

其實這個和寫一個Fragment是同樣的,可是也存在和上面彈出對話框的那種問題,就是當你佈局過大的狀況下會留出一段空間。

這裏主要說明兩點問題:

  • 使用這個獲取相應的BottomSheetBehaviorBottomSheetBehavior.from((View) view.getParent());
  • 使用show(getSupportFragmentManager(), "dialog");顯示。

2.2 SwipeDismissBehavior的使用

這個是滑動消失和滑動關閉,不少狀況下都是和5.0新出的Snackbar。一個和Toast相似的東西,由於不是本文重點,因此關於Snackbar就不展開說了!其實除了Snackbar使用到這個,基本上沒有那個APP想把本身的頁面劃沒了吧!!!其實用法仍是很簡單的,主要建立一個對象,設置一些像一個的參數就能夠了!直接上代碼:

TextView tvTitle = findViewById(R.id.tv_title);
        SwipeDismissBehavior<View> mSwipe = new SwipeDismissBehavior();
        /*
         * SWIPE_DIRECTION_START_TO_END 只能從左向右滑動
         * SWIPE_DIRECTION_END_TO_START 只能從右向左滑動
         * SWIPE_DIRECTION_ANY 左右滑動均可以
         */
        mSwipe.setSwipeDirection(SwipeDismissBehavior.SWIPE_DIRECTION_ANY);

        mSwipe.setListener(new SwipeDismissBehavior.OnDismissListener() {
            @Override
            public void onDismiss(View view) {
                //View消失的回調
            }

            @Override
            public void onDragStateChanged(int state) {
                /*
                 * STATE_IDLE 空閒狀態 
                 * STATE_DRAGGING 滑動中
                 * STATE_SETTLING 消失
                 */
            }
        });
複製代碼

註釋已經很詳細了,這裏注意一點啊,若是設置了滑動刪除功能,這個頁面就存在滑動刪除的功能了,是頁面存在這個功能,里門的大多數控件都能存在滑動刪除功能,可是我嘗試了,AppBarLayout等一些相應的控件不能,估計是設置了behavior的控件不能滑動刪除,其餘的均可以,可是這個只是個人猜想,沒有驗證!!!

3. Behavior裏面回調的說明:

這裏先明確一個概念,behavior的嵌套滾動都是依照一個相應的參考物,因此在自定義的時候必定要區分哪一個是依照的View哪一個是被觀察的View,只有區分了這些才能更好的理解下面的內容,下面出現的全部child都是被觀察的View,也就是xml中定義behavior的View。

  • layoutDependsOn(CoordinatorLayout parent, View child, View dependency) 表示是否給應用了Behavior 的View 指定一個依賴的佈局

    • 參數1:coordinatorlayout對象
    • 參數2:child 被觀察的View
    • 參數3:依賴變化的View(被觀察的View)
  • onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) 當依賴的View發生變化的時候hi掉的方法

  • onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) 當用戶手指按下的時候,你是否要處理此次操做。當你肯定要處理此次操做的時候,返回true;若是返回false的時候,就不會去響應後面的回調事件了。你想怎麼滑就怎麼話,我都不作處理。這裏的(axes)滾動方向很重要,能夠經過此參數判斷滾動方向!

    • 參數3:直接目標,至關於能滑動的控件
    • 參數4:觀察的View
    • 參數5:這個能夠簡單理解爲滾動方向
      • ViewCompat#SCROLL_AXIS_HORIZONTAL 水平方向
      • ViewCompat#SCROLL_AXIS_VERTICAL 豎直方向
    • 參數6:這個參數是以後有的,若是你輸入的類型不是TYPE_TOUCH那麼就不會相應這個滾動
  • onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, @ScrollAxis int axes, @NestedScrollType int type) 當onStartNestedScroll準備處理此次滑動的時候(返回true的時候),回調這個方法。能夠在這個方法中作一些響應的準備工做!

  • onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, @NestedScrollType int type) 當滾動開始執行的時候回調這個方法。

    • 參數4/參數5:用戶x/y軸滾動的距離(注意這裏是每一次都回調的啊!!!)
    • 參數6:處理滾動的距離的參數,內部維護着輸出距離,假設用戶滑動了100px,child 作了90px的位移,你須要把consumed[1]的值改爲90,這樣coordinatorLayout就能知道只處理剩下的10px的滾動。其中consumed[0]表明x軸、consumed[1]表明y軸。可能你不理解這個問題,換個形象點的比喻,好比你開發某一個功能,可是你只會其中的90%那麼怎麼辦呢?不能就無論了。好你找到了你的同事或者老大,讓他去完成剩下的10%。這樣問題就完美的解決了,是一個概念的!
  • onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) 上面這個方法結束的時候,coordinatorLayout處理剩下的距離,好比還剩10px。可是coordinatorLayout發現滾動2px的時候就已經到頭了。那麼結束其滾動,調用該方法,並將coordinatorLayout處理剩下的像素數做爲參(dxUnconsumed、dyUnconsumed)傳過來,這裏傳過來的就是 8px。參數中還會有coordinatorLayout處理過的像素數(dxConsumed、dyConsumed)。老大開始處理剩下的距離了!這個方法主要處理一些越界後的滾動。仍是不懂對吧!還拿大家老大作比喻:好比上面還剩 10%的工做,這時老大處理了2%後發現已經能夠上線了,因而老大結束了工做,並將處理剩下的內容(dxUnconsumed、dyUnconsumed)紀錄下來,告訴你。老大處理了的內容(dxConsumed、dyConsumed)也告訴了你。

    • 參數4/參數5:當沒有滾動到頂部或者底部的時候,x/y軸的滾動距離
    • 參數6/參數7:當滾動到頂部或者底部的時候,x/y軸的滾動距離
if (dyConsumed > 0 && dyUnconsumed == 0) {
    System.out.println("上滑中。。。");
}
if (dyConsumed == 0 && dyUnconsumed > 0) {
    System.out.println("到邊界了還在上滑。。。");
}
if (dyConsumed < 0 && dyUnconsumed == 0) {
    System.out.println("下滑中。。。");
}
if (dyConsumed == 0 && dyUnconsumed < 0) {
    System.out.println("到邊界了,還在下滑。。。");
}
複製代碼
  • onNestedPreFling(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, float velocityX, float velocityY) 當手指鬆開發生慣性動做以前調用,這裏提供了響應的速度,你能夠根據速度判斷是否須要進行摺疊等一系列的操做,你要肯定響應這個方法的話,返回true。

    • 參數4/參數5:表明相應的速度
  • onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) 中止滾動的時候回調的方法。當你不去響應Fling的時候會直接回調這個方法。在這裏能夠作一些清理工做。或者其餘的內容。。。

  • onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) 肯定子View位置的方法,這個方法能夠從新定義子View的位置(這裏明確是設置behavior的那個View哦),例以下面這樣

    • ViewCompat#LAYOUT_DIRECTION_LTR 視圖方向從左到右
    • ViewCompat#LAYOUT_DIRECTION_RTL 視圖方向從優到左

基本上能用到的API就這麼多,可是這裏面的內容不少,先好好理解一下,我其實都不怎麼理解,沒事不理解沒事,後面幾個例子就ok了!

我只是一個吃瓜羣衆

4. 幾個常見的Behavior的案例:

這裏說明一下自定義Behavior分爲兩種類型,一種是依賴相應的View變化而變化、一種是依賴滾動變化。

4.1 依賴於某個View的變化而變化的Behavior

這個最經典的案例就是底欄跟隨AppBarLayout移動給移動,其實代碼很簡單,只要算出AppBarLayout的移動距離,動態的設置給相應的依賴控件就能夠了。一波代碼走起!!!先上一張效果圖。

效果圖

public class TwoBehavior extends CoordinatorLayout.Behavior<View> {

    private String TAG = TwoBehavior.class.getSimpleName();

    //這個千萬要寫,不然會出異常
    public TwoBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        //依賴於AppBarLayout的
        return dependency instanceof AppBarLayout;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        //計算出AppBarLayout移動的距離
        float top = Math.abs(dependency.getTop());
        Log.e(TAG, "AppBarLayout移動的距離" + top);
        child.setTranslationY(top);
        return true;
    }
}
複製代碼

而後代碼裏使用:app:layout_behavior="全路徑"就能夠實現了,由於CoordinatorLayout裏面的內容怕你不知道,因此這裏我仍是貼一下相應的清單文件,要不你又該把實現不了的鍋讓我背了,這個鍋我不背。。。

<?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"
    tools:context="com.jinlong.newmaterialdesign.behavior.TwoBehaviorActivity">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <android.support.v7.widget.Toolbar
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="@color/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:title="底部聯動的Behavior"
            app:titleTextColor="@android:color/white" />
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.NestedScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="#009988"
                android:gravity="center"
                android:text="標籤1" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="#002288"
                android:gravity="center"
                android:text="標籤2" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="#009922"
                android:gravity="center"
                android:text="標籤3" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="#00aa88"
                android:gravity="center"
                android:text="標籤4" />

            <TextView
                android:layout_width="match_parent"
                android:layout_height="200dp"
                android:background="#999988"
                android:gravity="center"
                android:text="標籤5" />
        </LinearLayout>
    </android.support.v4.widget.NestedScrollView>

    <TextView
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_gravity="bottom"
        android:background="@color/colorPrimary"
        android:gravity="center"
        android:text="這個一個底欄"
        android:textColor="@android:color/white"
        app:layout_behavior="com.jinlong.newmaterialdesign.behavior.TwoBehavior" />
</android.support.design.widget.CoordinatorLayout>
複製代碼

這裏面處理View的變化纔是難點,反正我是這麼認爲的,不懂的同窗能夠補充一下相應的知識,什麼View變化,獲取相應位置的方法等些許內容。網上仍是挺多的,依賴的View基本上都是這麼實現的。都是實現這兩個方法的,玩轉了就行了,多寫寫天然就熟了。

4.2 依賴於滾動變化而變化的Behavior

這個就比較難了,由於涉及到相應的滾動計算什麼的,只有多寫多看才能熟,先來一個簡單的例子吧。剩下的就靠你們多多練習了!!!

4.2.1 首先是對位置的肯定

@Override
    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
        //設置了behavior的佈局
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        if(params!=null && params.height == CoordinatorLayout.LayoutParams.MATCH_PARENT){
            child.layout(0,0,parent.getWidth(),parent.getHeight());
            child.setTranslationY(getHeaderHeight());
            return true;
        }

        return super.onLayoutChild(parent, child, layoutDirection);
    }
    
    /**
     * 這裏是Header的高度,能夠設置成任何你想的高度
     */
    public int getHeaderHeight(){
    //      當你設置到相應的清單文件的時候,你就這麼弄
    //      return Context.getResources().getDimensionPixelOffset(R.dimen.header_height);
        return 500;
    }
複製代碼

這裏的位置肯定主要用到了一個View.setTranslationY()的方法,這個方法我查了查,和View.getTop()的方法有些相似,是相對於父控件左上角的偏移量。那麼就很好理解了,設置Behavior的View便宜到指定的位置下面,由於這裏設置了一個相應的圖片高度,因此這裏就是在圖片的下面。

4.2.2 處理相應的滾動事件(到難點了。這裏要好好看哦)

效果圖一張

  • 首先處理相應的滾動方向,由於這裏處理的滾動方向爲豎直方向,因此代碼是這個樣子滴!
@Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        //若是是豎直移動的話纔能有後面的響應的事件
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
    }
複製代碼
  • 處理滾動距離等一系列的計算

首先點你手指開始滑動的時候,會執行下面這個方法。

@Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
        // 在這個方法裏面只處理向上滑動
        if(dy < 0){
            return;
        }
        //計算每次移動的距離
        float transY =  child.getTranslationY() - dy;
        if(transY > 0){
            child.setTranslationY(transY);
            consumed[1]= dy;
        }
    }
複製代碼

說明一下:dy<0 表明的是向下滑動。child.getTranslationY()獲取的是設置behavior的View距離CoordinatorLayout頂部的偏移量。dy表明的是每一次移動的距離。因此transY計算的就是每一次移動後應該距離頂部的距離設置給相應的View。這裏consumed[1]= dy表示的是你處理的距離(後面會用到的)。因此就不難理解了,當你每次向上滑動的時候,會計算相應的數值,設置給child,使它逐漸的向上移動,當到達頂部以後就不進行改變相應的豎直了。就醬紫了...

每次上面的方法執行完成的時候會調用下面這個方法

@Override
    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
        // 在這個方法裏只處理向下滑動
        if(dyUnconsumed >0){
            return;
        }

        float transY = child.getTranslationY() - dyUnconsumed;
        Log.i(TAG,"------>transY:"+transY+"****** child.getTranslationY():"+child.getTranslationY()+"--->dyUnconsumed"+dyUnconsumed);
        if(transY > 0 && transY < getHeaderHeight()){
            child.setTranslationY(transY);
        }
    }
複製代碼

還記的我上面說的那個老大的問題吧?當你consumed[1]= dy的時候,就會傳遞過來相應的參數dxUnconsumed/dyUnconsumed表明處理剩下的參數,可是這裏要注意一點。針對於上面這個例子當你上一個方法return的時候,那麼這個dxUnconsumed/dyUnconsumed就會有值的!那就不難理解了,當你向下滑動的時候會之間改變child的位置,直到child所有顯示出來爲止!

4.2.3 所有的內容是醬紫的

public class OneBehavior extends CoordinatorLayout.Behavior {

    private String TAG = OneBehavior.class.getSimpleName();

    public OneBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onStopNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int type) {
        super.onStopNestedScroll(coordinatorLayout, child, target, type);
    }

    @Override
    public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
        //若是是水平移動的話響應響應的事件
        return axes == ViewCompat.SCROLL_AXIS_VERTICAL || super.onStartNestedScroll(coordinatorLayout, child, directTargetChild, target, axes, type);
    }

    @Override
    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
        // 在這個方法裏只處理向下滑動
        if(dyUnconsumed >0){
            return;
        }

        float transY = child.getTranslationY() - dyUnconsumed;
        Log.i(TAG,"------>transY:"+transY+"****** child.getTranslationY():"+child.getTranslationY()+"--->dyUnconsumed"+dyUnconsumed);
        if(transY > 0 && transY < getHeaderHeight()){
            child.setTranslationY(transY);
        }
    }

    @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
        // 在這個方法裏面只處理向上滑動
        if(dy < 0){
            return;
        }

        float transY =  child.getTranslationY() - dy;
        Log.i(TAG,"transY:"+transY+"++++child.getTranslationY():"+child.getTranslationY()+"---->dy:"+dy);
        if(transY > 0){
            child.setTranslationY(transY);
            consumed[1]= dy;
        }
    }

    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, View child, int layoutDirection) {
        //設置了behavior的佈局
        CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        if(params!=null && params.height == CoordinatorLayout.LayoutParams.MATCH_PARENT){
            child.layout(0,0,parent.getWidth(),parent.getHeight());
            child.setTranslationY(getHeaderHeight());
            return true;
        }

        return super.onLayoutChild(parent, child, layoutDirection);
    }

    /**
     * 這裏是Header的高度,能夠設置成任何你想的高度
     */
    public int getHeaderHeight(){
    //        當你設置到相應的清單文件的時候,你就這麼弄
    //        return Context.getResources().getDimensionPixelOffset(R.dimen.header_height);
        return 500;
    }
}
複製代碼

4.2.3 佈局的內容

佈局的內容就簡單多了。直接引入一個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="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="500px"
        android:scaleType="centerCrop"
        android:src="@mipmap/heard_1" />
    <!--這是一張圖片-->

    <android.support.v4.widget.NestedScrollView
        android:id="@+id/nested_scroll_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white"
        app:layout_behavior="com.jinlong.newmaterialdesign.behavior.OneBehavior">
    <!--這是是全路徑-->
        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:text="@string/large_text" />
    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
複製代碼

基本上就這麼多了,其實我在算法上面真的很渣、很渣一個很渣已經不能形容我了。我已經很努力的給大家講明白了。不懂的能夠給我留言,其實自定義Behavior真的是看見一個你練一個,慢慢的你就能開車了!其實我也是看了別人分享的東西以後總結的。有什麼很差的地方還請多多指教!關於Behavior原理感興趣的同窗能夠看看HelloCsld的這篇文章講的也是挺透徹的!但願今天講的這些能幫到你!今天就到這裏吧!拜拜。。。

代碼在gitHub上的地址感興趣的同窗能夠去看看!!!

搞笑一下
相關文章
相關標籤/搜索