學(xué)習(xí)CoordinatorLayout你需要知道的那些事

隨著Android M的發(fā)布同時也帶來了新的控件CoordinatorLayout.

想要你的各控件之間有很好的"聯(lián)動性"動畫效果,學(xué)會使用這個控件能幫你解決這個問題.

下面一步步的去了解CoordinatorLayout控件怎么用:

CoordinatorLayout

CoordinatorLayout的作用是什么?

CoordinatorLayout是用來協(xié)調(diào)其子view們之間動作的一個父view

  • CoordinatorLayout須作為頂層父View
  • 子View想要與CoordinatorLayout實現(xiàn)"聯(lián)動性"效果的首要條件是這個View必須實現(xiàn)了NestedScrollingChild接口 RecyclerView俘侠,NestedScrollView等新的控件都實現(xiàn)這個接口,ListView這些舊的控件就無法使用了.
  • 只有CoordinatorLayout的直接子View才會有效果,子View的子View無效

Behavior

Behavior的作用是什么?

Behavior用于當(dāng)前控件的父控件CoordinatorLayout中的其他子控件的關(guān)系

  • 使用Behavior的控件必須是直接從屬于CoordinatorLayout,否者無效

  • 自定義Behavior的時候必須覆蓋下面的構(gòu)造方法,因為通過反射實例化的時候用的就是該構(gòu)造方法.

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

AppBarLayout

AppBarLayout下的子View,注意是AppBarLayout如果設(shè)置了layout_scrollFlags="snap",但是Viewpager沒有設(shè)置layout_behavior,View一樣會自動隱藏,但是是動畫形式,而不是跟隨手機(jī)移動距離進(jìn)行判斷,并且AppBar控件會顯示在ViewPager的上面擋住,Viewpager設(shè)置了layout_scrollFlags以后會恢復(fù)正常.

AppBarLayout的作用是什么?

作為一個容器把AppBarLayout內(nèi)的所有子View"合并"成一個整體的View.

百度搜出來的效果圖
百度搜出來的效果圖

上圖可以看到ToolBar和TabLayout是作為一個"整體存在的",他們都被父控件AppBarLayout包裹

有了上面CoordinatorLayout,Behavior,AppBarLayout的了解,現(xiàn)在來講他們怎么配合使用

要執(zhí)行嵌套滾動,三者需要配合使用,可以說三者是缺一不可.

layout_scrollFlags屬性

AppBarLayout下的View想實現(xiàn)聯(lián)動效果必須設(shè)置該屬性,其中主要的參數(shù)包括下面幾個:

  1. scroll - 想滾動就必須設(shè)置這個弥雹。
  2. EnterAlways - 當(dāng)依賴的View向下滑動時,設(shè)置該屬性的View以相同的方向滑動直到消失
  3. exitUntilCollapsed - 配合minHeight使用.假設(shè)view的Height是300px,minHeight是200px,控件展開的話高度是300px,收縮了后最小高度會停留在200px而不是完全隱藏
  4. enterAlwaysCollapsed:假設(shè)view的Height是300px,minHeight是200px,這時候依賴控件向下滑動,view會隨著依賴view慢慢出現(xiàn),直到view顯示了200px后停止滑動.直到依賴控件滑到了頂部再次往下滑動剩下的100px才會出現(xiàn).(有點類似下拉刷新,必須滑到第一個item再下拉才能刷新)

先來看源碼:

<?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:id="@+id/behavior_demo_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >


    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_scrollFlags="scroll|enterAlways|snap"
        >
    </android.support.v7.widget.Toolbar>

</android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/behavior_demo_recycler"
        android:layout_width="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        android:layout_height="match_parent"
        />
        
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="?attr/actionBarSize"
    android:layout_gravity="bottom"
    android:background="@color/colorPrimary"
    android:gravity="center"
    app:layout_behavior="com.lipt0n.coordinatorlayout.MyBottomBarBehavior">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="底部菜單"
        android:textColor="#ffffff"/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>

可以看到RecyclerView的屬性指定了

'app:layout_behavior="@string/appbar_scrolling_view_behavior"'

作用是: 我們要讓AppBarLayout與RecyclerView之間產(chǎn)生聯(lián)動,需要在屬性中添加app:layout_behavior這個屬性,這樣兩者之間就有了關(guān)聯(lián).support library包含了一個特殊的字符串資源@string/appbar_scrolling_view_behavior,它指向的是就是AppBarLayout.ScrollingViewBehavior這個類袍暴,用來通知AppBarLayout,RecyclerView什么時候發(fā)生了滾動,讓AppBarLayout計算出合適的值來配合RecyclerView產(chǎn)生聯(lián)動.

需要注意的是,RecyclerView和AppBarLayout必須是同級的View,同時必須是為CoordinatorLayout下的子View才能產(chǎn)生聯(lián)動效果.

大白話就是:當(dāng)CoordinatorLayout發(fā)現(xiàn)RecyclerView中定義了app:layout_behavior="@string/appbar_scrolling_view_behavior"這個屬性,它會搜索自己所包含的其他view
看看是否有view與這個behavior相關(guān)聯(lián)奕删。AppBarLayout.ScrollingViewBehavior描述了
RecyclerView與AppBarLayout之間的依賴關(guān)系坠韩。RecyclerView的任意滾動事件都將觸發(fā)
AppBarLayout或者AppBarLayout里面view的改變(自動的,我們不需要去干預(yù))

代碼中的最下面還有一個LinearLayout里面包含了一個TextView,其中LinearLayout定義了app:layout_behavior="com.lipt0n.coordinatorlayout.MyBottomBarBehavior",也就是說我們自定義了一個behavior,接下來看一下代碼:

自定義Behavior1

    package com.lipt0n.coordinatorlayout;
    
    import android.content.Context;
    import android.support.design.widget.AppBarLayout;
    import android.support.design.widget.CoordinatorLayout;
    import android.util.AttributeSet;
    import android.view.View;
    public class MyBottomBarBehavior extends CoordinatorLayout.Behavior<View> {
    
    
    
    public MyBottomBarBehavior(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    
    /**
     * 只要是CoordinatorLayout內(nèi)的View的狀態(tài)發(fā)送了變化,該方法就會執(zhí)行
     * @param parent 頂層父控件CoordinatorLayout
     * @param child 我們設(shè)置這個Behavior的View
     * @param dependency View的值會不斷的變化,他會輪詢CoordinatorLayout下所有所屬的子View
     * @return 我們需要在這里判斷dependency所屬的View是哪一個,如果是ture執(zhí)行
    onDependentViewChanged,否則不執(zhí)行
     */
    @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {

         聽不懂dependency值的意思的可以去掉下面兩行代碼在控制臺觀察輸出的結(jié)果
        //System.out.println("child"+child.getClass());
        //System.out.println("dependency"+dependency.getClass());
        
        return dependency instanceof AppBarLayout;
    }
    
    /*參數(shù)和上面的一致*/
    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
    
        /*
         *這里獲取dependency的top值,也就是AppBarLayout的top,因為AppBarLayout
         *在是向上滾出界面的,我們的因為是和AppBarLayout相反,所以取絕對值.
         */
        float translationY = Math.abs(dependency.getTop());
        child.setTranslationY(translationY);
        return true;
    }
    
    }

自定義Behavior2

public class MyFabBehavior extends CoordinatorLayout.Behavior<View> {

    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();

    private float viewY;//控件距離coordinatorLayout底部距離
    private boolean isAnimate;//動畫是否在進(jìn)行

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

    /**
     * 有嵌套滑動到來了茧泪,問下該Behavior是否接受嵌套滑動
     *
     * @param coordinatorLayout 當(dāng)前的CoordinatorLayout
     * @param child             該Behavior對應(yīng)的View
     * @param directTargetChild 我的理解是在CoordinateLayout下作為父View,而該View的子類是Tager的那個View,也就是Target的父View),因為我測試用ViewPager包裹了RecycleView后該參數(shù)返回Viewpager,如果沒有包裹參數(shù)返回的是RecycleView
     * @param target            具體嵌套滑動的那個子類
     * @param nestedScrollAxes  支持嵌套滾動軸。水平方向圆恤,垂直方向突倍,或者不指定
     * @return 是否接受該嵌套滑動
     */

    @Override
    public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, View child, View directTargetChild, View target, int nestedScrollAxes) {

        System.out.println("child"+child);
        System.out.println("target"+target);
        System.out.println("directTargetChild"+directTargetChild);
        if(child.getVisibility() == View.VISIBLE&&viewY==0){
            //獲取控件距離父布局(coordinatorLayout)底部距離
            viewY=coordinatorLayout.getHeight()-child.getY();
        }

        //ViewCompat是一個兼容類,在android5.0之前的API為了實現(xiàn)新的效果
        //避免出錯使用ViewCompat.xxxx方法可以解決出現(xiàn)低版本錯誤的問題
        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;//判斷是否豎直滾動
    }

    /**
     * 在嵌套滑動的子View未滑動之前準(zhǔn)備滑動的情況  (待修改)
     *
     * @param coordinatorLayout
     * @param child             該Behavior對應(yīng)的View
     * @param target            具體嵌套滑動的那個子類
     * @param dx                水平方向嵌套滑動的子View想要變化的距離
     * @param dy                垂直方向嵌套滑動的子View想要變化的距離
     * @param consumed          這個參數(shù)要我們在實現(xiàn)這個函數(shù)的時候指定,回頭告訴子View當(dāng)前父View消耗的距離 consumed[0] 水平消耗的距離盆昙,consumed[1] 垂直消耗的距離 好讓子view做出相應(yīng)的調(diào)整
     */
    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        //dy大于0是向上滾動 小于0是向下滾動
        //System.out.println(dy);
        if (dy >=0&&!isAnimate&&child.getVisibility()==View.VISIBLE) {
            hide(child);
        } else if (dy <0&&!isAnimate&&child.getVisibility()==View.INVISIBLE) {
            show(child);
        }
    }

    //隱藏時的動畫
    private void hide(final View view) {
        ViewPropertyAnimator animator = view.animate().translationY(viewY).setInterpolator(INTERPOLATOR).setDuration(500);

        animator.setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                isAnimate=true;
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                view.setVisibility(View.INVISIBLE);
                isAnimate=false;
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                show(view);
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });
        animator.start();
    }

    //顯示時的動畫
    private void show(final View view) {
        ViewPropertyAnimator animator = view.animate().translationY(0).setInterpolator(INTERPOLATOR).setDuration(500);
        animator.setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
                view.setVisibility(View.VISIBLE);
                isAnimate=true;
            }

            @Override
            public void onAnimationEnd(Animator animator) {
                isAnimate=false;
            }

            @Override
            public void onAnimationCancel(Animator animator) {
                hide(view);
            }

            @Override
            public void onAnimationRepeat(Animator animator) {
            }
        });
        animator.start();
    }
}
ezgif.com-optimize.gif

兩個自定義Behavior的效果都圖都差不多,上圖是Behavior2的效果,兩者的區(qū)別是前者隨著AppBarLayout的滑動一起滑動,后者是dy大于0執(zhí)行隱藏動畫 小于0執(zhí)行顯示動畫

只要理解了各個控件的作用,其實用起來并不難.

TabLayout

TabLayout的作用是什么?

作用就是實現(xiàn)上一張gif圖的效果,在Toolbar下面顯示多個Tab.

直接看源碼:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/drawlayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#fff"
>


/*
* Google官方的側(cè)滑菜單控件和本文知識點無法,可以無視.
*/
<android.support.design.widget.NavigationView
    android:id="@+id/nv_left_content"
    android:layout_width="wrap_content"
    android:layout_height="match_parent"
    android:layout_gravity="left"
    android:background="#696969"
    app:headerLayout="@layout/fragment_navigation"
    app:menu="@menu/left_home_botton"
    />



    <android.support.design.widget.CoordinatorLayout

    android:id="@+id/behavior_demo_coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#fff">


    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >


        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?android:actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways|snap"
            app:title="CoordinatorLayout"
            >
        </android.support.v7.widget.Toolbar>

        <android.support.design.widget.TabLayout
            android:id="@+id/tablayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"

            app:tabIndicatorColor="@color/colorAccent">

            <android.support.design.widget.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tab1"/>

            <android.support.design.widget.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tab2"/>

            <android.support.design.widget.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tab3"/>

            <android.support.design.widget.TabItem
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="tab4"/>

        </android.support.design.widget.TabLayout>

    </android.support.design.widget.AppBarLayout>


    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        ></android.support.v4.view.ViewPager>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_gravity="bottom"
        android:background="@color/colorPrimary"
        android:gravity="center"
        app:layout_behavior="com.lipt0n.coordinatorlayout.MyFabBehavior">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:text="底部菜單"
            android:textColor="#ffffff"/>
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>
</android.support.v4.widget.DrawerLayout>

看到上面的AppBarLayout部分的代碼了?再次體現(xiàn)了AppBarLayout下子View作為一個整體的作用

screenrecorder_Trimmed_20170207_135046.gif

注意點:可以看到TabLayout沒有設(shè)置app:layout_scrollFlags屬性,原因是我只希望ToolBar進(jìn)行滑動隱藏,TabLayout只是跟隨ToolBar滑動不隱藏.(看不懂文字的可以看gif圖),想要讓TabLayout也隱藏加上app:layout_scrollFlags屬性.

代碼部分簡單講一下:

就是先對ViewPager進(jìn)行適配,然后實例化TabLayout,然后調(diào)用tablayout.setupWithViewPager(viewPager)讓tablayout和Viewpager進(jìn)行關(guān)聯(lián),這樣滑動viewpager,tablayout的指示器就會跟著改變,點擊tablayout的指示器,Viewpager也會切換.

還有其他的內(nèi)容有需要的話會在下一篇的文章會接著寫羽历。

如果文章有錯誤的地方希望能指出,避免誤導(dǎo)他人.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淡喜,隨后出現(xiàn)的幾起案子秕磷,更是在濱河造成了極大的恐慌,老刑警劉巖拆火,帶你破解...
    沈念sama閱讀 222,378評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件跳夭,死亡現(xiàn)場離奇詭異涂圆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)币叹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,970評論 3 399
  • 文/潘曉璐 我一進(jìn)店門润歉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人颈抚,你說我怎么就攤上這事踩衩。” “怎么了贩汉?”我有些...
    開封第一講書人閱讀 168,983評論 0 362
  • 文/不壞的土叔 我叫張陵驱富,是天一觀的道長。 經(jīng)常有香客問我匹舞,道長褐鸥,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,938評論 1 299
  • 正文 為了忘掉前任赐稽,我火速辦了婚禮叫榕,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘姊舵。我一直安慰自己晰绎,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,955評論 6 398
  • 文/花漫 我一把揭開白布括丁。 她就那樣靜靜地躺著荞下,像睡著了一般。 火紅的嫁衣襯著肌膚如雪史飞。 梳的紋絲不亂的頭發(fā)上尖昏,一...
    開封第一講書人閱讀 52,549評論 1 312
  • 那天,我揣著相機(jī)與錄音祸憋,去河邊找鬼会宪。 笑死,一個胖子當(dāng)著我的面吹牛蚯窥,可吹牛的內(nèi)容都是我干的掸鹅。 我是一名探鬼主播,決...
    沈念sama閱讀 41,063評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼拦赠,長吁一口氣:“原來是場噩夢啊……” “哼巍沙!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起荷鼠,我...
    開封第一講書人閱讀 39,991評論 0 277
  • 序言:老撾萬榮一對情侶失蹤句携,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后允乐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矮嫉,經(jīng)...
    沈念sama閱讀 46,522評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡削咆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,604評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蠢笋。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拨齐。...
    茶點故事閱讀 40,742評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖昨寞,靈堂內(nèi)的尸體忽然破棺而出瞻惋,到底是詐尸還是另有隱情,我是刑警寧澤援岩,帶...
    沈念sama閱讀 36,413評論 5 351
  • 正文 年R本政府宣布歼狼,位于F島的核電站,受9級特大地震影響享怀,放射性物質(zhì)發(fā)生泄漏羽峰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,094評論 3 335
  • 文/蒙蒙 一凹蜈、第九天 我趴在偏房一處隱蔽的房頂上張望限寞。 院中可真熱鬧,春花似錦仰坦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,572評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凿滤,卻和暖如春妈橄,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背翁脆。 一陣腳步聲響...
    開封第一講書人閱讀 33,671評論 1 274
  • 我被黑心中介騙來泰國打工眷蚓, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人反番。 一個月前我還...
    沈念sama閱讀 49,159評論 3 378
  • 正文 我出身青樓沙热,卻偏偏與公主長得像,于是被迫代替她去往敵國和親罢缸。 傳聞我的和親對象是個殘疾皇子篙贸,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,747評論 2 361

推薦閱讀更多精彩內(nèi)容