一個神奇的控件——Android CoordinatorLayout與Behavior使用指南

介紹

CoordinatorLayout是用來協(xié)調(diào)其子view們之間動作的一個父view,而Behavior就是用來給CoordinatorLayout的子view們實現(xiàn)交互的赃额。


SUM

1. CollapsingToolbarLayout_伸縮折疊工具

參考:看熄守,這個工具欄能伸縮折疊——Android CollapsingToolbarLayout使用介紹

a. CollapsingToolbarLayout折疊或展開時励幼,F(xiàn)loatingActionButton跟隨運動并且大小相應變化.CollapsingToolbarLayout是專門用來實現(xiàn)子布局內(nèi)不同元素響應滾動細節(jié)的布局。
b. AppBarLayout是一種支持響應滾動手勢的app bar布局(比如工具欄滾出或滾入屏幕)雕什;與AppBarLayout組合的滾動布局(Recyclerview缠俺、NestedScrollView等)需要設置app:layout_behavior="@string/appbar_scrolling_view_behavior"(上面代碼中NestedScrollView控件所設置的)。沒有設置的話贷岸,AppBarLayout將不會響應滾動布局的滾動事件壹士。。
c. CollapsingToolbarLayout和ScrollView一起使用會有滑動bug偿警,注意要使用NestedScrollView來替代ScrollView躏救。

Android studio中有一個Activity模板叫ScrollingActivity,它實現(xiàn)的就是簡單的可折疊工具欄螟蒸。



ScrollingActivity的布局代碼如下:

<?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:fitsSystemWindows="true"
    tools:context="com.jack.jack_junit_demo.ScrollingActivity">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:fitsSystemWindows="true"
        android:layout_height="180dp"
        android:layout_width="match_parent"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:fitsSystemWindows="true"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:contentScrim="?attr/colorPrimary">

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_height="?attr/actionBarSize"
                android:layout_width="match_parent"
                app:layout_collapseMode="pin"
                app:popupTheme="@style/AppTheme.PopupOverlay" />

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>
    <!--可以include 抽取出來-->
    <android.support.v4.widget.NestedScrollView                    
            android:layout_width="match_parent"        
            android:layout_height="match_parent"              
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            >    
        <TextView        
                android:layout_width="wrap_content"        
                android:layout_height="wrap_content"        
                android:layout_margin="@dimen/text_margin"         
                android:text="@string/large_text" />     
    </android.support.v4.widget.NestedScrollView>

    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/fab_margin"
        app:layout_anchor="@id/app_bar"
        app:layout_anchorGravity="bottom|end"
        android:src="@android:drawable/ic_dialog_email" />

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

2.AppBarLayout的子布局有5種滾動標識

就是上面代碼CollapsingToolbarLayout中配置的app:layout_scrollFlags屬性:

  1. scroll:將此布局和滾動時間關聯(lián)盒使。這個標識要設置在其他標識之前,沒有這個標識則布局不會滾動且其他標識設置無效七嫌。
  2. enterAlways:任何向下滾動操作都會使此布局可見少办。這個標識通常被稱為“快速返回”模式。
  3. enterAlwaysCollapsed:假設你定義了一個最小高度(minHeight)同時enterAlways也定義了诵原,那么view將在到達這個最小高度的時候開始顯示英妓,并且從這個時候開始慢慢展開挽放,當滾動到頂部的時候展開完。
  4. exitUntilCollapsed:當你定義了一個minHeight蔓纠,此布局將在滾動到達這個最小高度的時候折疊骂维。
  5. snap:當一個滾動事件結束,如果視圖是部分可見的贺纲,那么它將被滾動到收縮或展開航闺。例如,如果視圖只有底部25%顯示猴誊,它將折疊潦刃。相反,如果它的底部75%可見懈叹,那么它將完全展開乖杠。
3.CollapsingToolbarLayout的contentScrim、statusBarScrim 屬性澄成。
  • app:contentScrim設置折疊時工具欄布局的顏色
  • app:statusBarScrim設置折疊時狀態(tài)欄的顏色胧洒。
    默認contentScrim是colorPrimary的色值,statusBarScrim是colorPrimaryDark的色值墨状。這個后面會用到卫漫。
4.CollapsingToolbarLayout子布局設置折疊模式,app:layout_collapseMode**
  • off:這個是默認屬性肾砂,布局將正常顯示列赎,沒有折疊的行為。
  • pin:CollapsingToolbarLayout折疊后镐确,此布局將固定在頂部包吝。
  • parallax:CollapsingToolbarLayout折疊時,此布局也會有視差折疊效果

當CollapsingToolbarLayout的子布局設置了parallax模式時源葫,我們還可以通過
app:layout_collapseParallaxMultiplier
設置視差滾動因子诗越,值為:0~1。

5.FloatingActionButton

FloatingActionButton這個控件通過app:layout_anchor這個設置錨定在了AppBarLayout下方息堂。FloatingActionButton源碼中有一個Behavior方法嚷狞,當AppBarLayout收縮時,F(xiàn)loatingActionButton就會跟著做出相應變化储矩。關于CoordinatorLayout和Behavior感耙,我下一篇文章會和大家一起學習。

B站很早就開源了一個彈幕引擎持隧,還起了個狂拽酷炫吊炸天的名字叫“烈焰彈幕使 ”(一看就是二次元程序猿們的作品→_→)即硼,源碼在github上,項目名叫DanmakuFlameMaster屡拨。


2. 自定義CoordinatorLayout的Behavior

自定義Behavior模仿知乎
參考:http://www.reibang.com/p/488283f74e69

1.先看下布局

<?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.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:layout_scrollFlags="scroll|enterAlways|snap"
            android:background="?attr/colorPrimary" />
    </android.support.design.widget.AppBarLayout>

    <android.support.v4.widget.SwipeRefreshLayout
        android:id="@+id/behavior_demo_swipe_refresh"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">
        <android.support.v7.widget.RecyclerView
            android:id="@+id/behavior_demo_recycler"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />
    </android.support.v4.widget.SwipeRefreshLayout>

    <!--行為只酥,依賴于自定義Beavior-->
    <android.support.design.widget.FloatingActionButton
        android:id="@+id/fab"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginRight="16dp"
        android:layout_marginBottom="72dp"
        android:src="@android:drawable/ic_dialog_email"
        app:layout_behavior="com.example.zcp.coordinatorlayoutdemo.behavior.MyFabBehavior"
        android:layout_gravity="bottom|right" />

    <!--行為褥实,依賴于自定義Beavior-->
    <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.example.zcp.coordinatorlayoutdemo.behavior.MyBottomBarBehavior">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:textColor="#ffffff"
            android:text="這是一個底欄"/>
    </LinearLayout>

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

SwipeRefreshLayout、FloatingActionButton和當做底欄的LinearLayout上有一個app:layout_behavior配置裂允。

SwipeRefreshLayout配置的"@string/appbar_scrolling_view_behavior"是系統(tǒng)提供的损离,用來使滑動控件與AppBarLayout互動。

FloatingActionButton和底欄上配置的是我們接下來要自定義的Behavior绝编。

先看FloatingActionButton的Behavior僻澎。

public class MyFabBehavior extends CoordinatorLayout.Behavior<View> {

    private static final Interpolator INTERPOLATOR = new FastOutSlowInInterpolator();

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

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

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

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

        return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;//判斷是否豎直滾動
    }

    @Override
    public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, View child, View target, int dx, int dy, int[] consumed) {
        //大于0是向上滾動 小于0是向下滾動

        if (dy >= 0 && !isAnimate && child.getVisibility() == View.VISIBLE) {
            hide(child);
        } else if (dy < 0 && !isAnimate && child.getVisibility() == View.GONE) {
            show(child);
        }
    }

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

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

            @Override
            public void onAnimationEnd(Animator animator) {
                view.setVisibility(View.GONE);
                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(200);
        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();
    }
}

邏輯并不復雜,我們通過重寫B(tài)ehavior中關于嵌套滑動的兩個回調(diào)完成了FloatingActionButton的隱藏和顯示判斷及操作十饥。

單獨出場的底欄也可以利用上面一樣的方法來設置隱藏或顯示窟勃,我的底欄是和AppBarLayout一起出場,所以我就讓底欄從屬于AppBarLayout活動逗堵。代碼如下:

public class MyBottomBarBehavior extends CoordinatorLayout.Behavior<View> {


    public MyBottomBarBehavior(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) {

        float translationY = Math.abs(dependency.getTop());//獲取更隨布局的頂部位置
        child.setTranslationY(translationY);
        return true;
    }

}

代碼量比上個還少秉氧。我們還可以通過重寫onMeasureChild()來使控件響應從屬控件的大小變化。

Behavior提供的很多蜒秤,我這里用到的只是一部分汁咏,大家可以看看源碼,根據(jù)具體需求去使用作媚。


3 .CollapsingToolbarLayout與TabLayout結合

CollapsingToolbarLayout與TabLayout組合使用的效果也不錯攘滩。


<?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:fitsSystemWindows="true">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="250dp"
        android:fitsSystemWindows="true"
        android:theme="@style/AppTheme.AppBarOverlay">

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/toolbar_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:titleEnabled="false"
            android:fitsSystemWindows="true"
            app:contentScrim="@color/colorPrimary"
            app:statusBarScrim="@android:color/transparent"
            app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
            <ImageView
                android:id="@+id/imageview"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:scaleType="centerCrop"
                android:adjustViewBounds="true"
                app:layout_collapseMode="parallax"
                app:layout_collapseParallaxMultiplier="0.7"
                android:fitsSystemWindows="true"
                android:src="@drawable/girl2"/>
            <View
                android:layout_width="match_parent"
                android:layout_height="40dp"
                android:background="@drawable/gradient"
                android:fitsSystemWindows="true" />
            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar"
                android:layout_width="match_parent"
                android:layout_height="96dp"
                android:minHeight="?attr/actionBarSize"
                android:gravity="top"
                app:layout_collapseMode="pin"
                app:title="hello"
                app:popupTheme="@style/AppTheme.PopupOverlay"
                app:titleMarginTop="15dp"
                />
            <android.support.design.widget.TabLayout
                android:id="@+id/tablayout"
                android:layout_width="match_parent"
                android:layout_height="45dp"
                android:layout_gravity="bottom" />
        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>


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

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

2. AppBarLayout
  1. SwipeRefreshLayout配置的"@string/appbar_scrolling_view_behavior"是系統(tǒng)提供的,用來使滑動控件與AppBarLayout互動掂骏。

參考資料

一個神奇的控件——Android CoordinatorLayout與Behavior使用指南

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末轰驳,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子弟灼,更是在濱河造成了極大的恐慌,老刑警劉巖冒黑,帶你破解...
    沈念sama閱讀 210,978評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件田绑,死亡現(xiàn)場離奇詭異,居然都是意外死亡抡爹,警方通過查閱死者的電腦和手機掩驱,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,954評論 2 384
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冬竟,“玉大人欧穴,你說我怎么就攤上這事”门梗” “怎么了涮帘?”我有些...
    開封第一講書人閱讀 156,623評論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長笑诅。 經(jīng)常有香客問我调缨,道長疮鲫,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,324評論 1 282
  • 正文 為了忘掉前任弦叶,我火速辦了婚禮俊犯,結果婚禮上,老公的妹妹穿的比我還像新娘伤哺。我一直安慰自己燕侠,他們只是感情好,可當我...
    茶點故事閱讀 65,390評論 5 384
  • 文/花漫 我一把揭開白布立莉。 她就那樣靜靜地躺著绢彤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桃序。 梳的紋絲不亂的頭發(fā)上杖虾,一...
    開封第一講書人閱讀 49,741評論 1 289
  • 那天,我揣著相機與錄音媒熊,去河邊找鬼奇适。 笑死,一個胖子當著我的面吹牛芦鳍,可吹牛的內(nèi)容都是我干的嚷往。 我是一名探鬼主播,決...
    沈念sama閱讀 38,892評論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼柠衅,長吁一口氣:“原來是場噩夢啊……” “哼皮仁!你這毒婦竟也來了?” 一聲冷哼從身側響起菲宴,我...
    開封第一講書人閱讀 37,655評論 0 266
  • 序言:老撾萬榮一對情侶失蹤贷祈,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后喝峦,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體势誊,經(jīng)...
    沈念sama閱讀 44,104評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,451評論 2 325
  • 正文 我和宋清朗相戀三年谣蠢,在試婚紗的時候發(fā)現(xiàn)自己被綠了粟耻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,569評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡眉踱,死狀恐怖挤忙,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情谈喳,我是刑警寧澤册烈,帶...
    沈念sama閱讀 34,254評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站叁执,受9級特大地震影響茄厘,放射性物質(zhì)發(fā)生泄漏矮冬。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,834評論 3 312
  • 文/蒙蒙 一次哈、第九天 我趴在偏房一處隱蔽的房頂上張望胎署。 院中可真熱鬧,春花似錦窑滞、人聲如沸琼牧。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,725評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巨坊。三九已至,卻和暖如春此改,著一層夾襖步出監(jiān)牢的瞬間趾撵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,950評論 1 264
  • 我被黑心中介騙來泰國打工共啃, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留占调,地道東北人。 一個月前我還...
    沈念sama閱讀 46,260評論 2 360
  • 正文 我出身青樓移剪,卻偏偏與公主長得像究珊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子纵苛,可洞房花燭夜當晚...
    茶點故事閱讀 43,446評論 2 348

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