CollapsingToolbarLayout使用介紹

我非常喜歡Material Design裏摺疊工具欄的效果,bilibili Android客戶端視頻詳情頁就是採用的這種設計。這篇文章的第二部分我們就通過簡單的模仿bilibili視頻詳情頁的實現來了解下CollapsingToolbarLayout的使用。文章的第三部分介紹了CollapsingToolbarLayout與TabLayout的組合使用。

有基礎的朋友可以直接跳過第一部分。

一、相關基礎屬性介紹

Android studio中有一個Activity模板叫ScrollingActivity,它實現的就是簡單的可摺疊工具欄,我們將此模板添加到項目中。

strip
ScrollingActivity.gif

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">

<android.support.design.widget.AppBarLayout
    android:id="@+id/app_bar"
    android:layout_width="match_parent"
    android:layout_height="@dimen/app_bar_height"
    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"
        android:fitsSystemWindows="true"
        app:contentScrim="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|exitUntilCollapsed">

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

    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<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"
    android:src="@android:drawable/ic_dialog_email"
    app:layout_anchor="@id/app_bar"
    app:layout_anchorGravity="bottom|end" />

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

AppBarLayout是一種支持響應滾動手勢的app bar佈局(比如工具欄滾出或滾入屏幕),CollapsingToolbarLayout則是專門用來實現子佈局內不同元素響應滾動細節的佈局。

與AppBarLayout組合的滾動佈局(Recyclerview、NestedScrollView等)需要設置app:layout_behavior="@string/appbar_scrolling_view_behavior"(上面代碼中NestedScrollView控件所設置的)。沒有設置的話,AppBarLayout將不會響應滾動佈局的滾動事件。

CollapsingToolbarLayout和ScrollView一起使用會有滑動bug,注意要使用NestedScrollView來替代ScrollView。

AppBarLayout的子佈局有5種滾動標識(就是上面代碼CollapsingToolbarLayout中配置的app:layout_scrollFlags屬性):

  1. scroll:將此佈局和滾動時間關聯。這個標識要設置在其他標識之前,沒有這個標識則佈局不會滾動且其他標識設置無效。
  2. enterAlways:任何向下滾動操作都會使此佈局可見。這個標識通常被稱爲“快速返回”模式。
  3. enterAlwaysCollapsed:假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那麼view將在到達這個最小高度的時候開始顯示,並且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
  4. exitUntilCollapsed:當你定義了一個minHeight,此佈局將在滾動到達這個最小高度的時候摺疊。
  5. snap:當一個滾動事件結束,如果視圖是部分可見的,那麼它將被滾動到收縮或展開。例如,如果視圖只有底部25%顯示,它將摺疊。相反,如果它的底部75%可見,那麼它將完全展開。

CollapsingToolbarLayout可以通過app:contentScrim設置摺疊時工具欄佈局的顏色,通過app:statusBarScrim設置摺疊時狀態欄的顏色。默認contentScrim是colorPrimary的色值,statusBarScrim是colorPrimaryDark的色值。這個後面會用到。

CollapsingToolbarLayout的子佈局有3種摺疊模式(Toolbar中設置的app:layout_collapseMode)

  1. off:這個是默認屬性,佈局將正常顯示,沒有摺疊的行爲。
  2. pin:CollapsingToolbarLayout摺疊後,此佈局將固定在頂部。
  3. parallax:CollapsingToolbarLayout摺疊時,此佈局也會有視差摺疊效果。

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

FloatingActionButton這個控件通過app:layout_anchor這個設置錨定在了AppBarLayout下方。FloatingActionButton源碼中有一個Behavior方法,當AppBarLayout收縮時,FloatingActionButton就會跟着做出相應變化。關於CoordinatorLayout和Behavior,我下一篇文章會和大家一起學習。

這一堆屬性看着有點煩,大家可以新建一個ScrollingActivity模板去實驗一下玩玩。

二、模仿bilibili客戶端視頻詳情頁

我們先對原界面分析一下。

strip
嗶哩嗶哩Android客戶端視頻詳情頁.gif

界面初始,CollapsingToolbarLayout是展開狀態,顯示的是視頻封面。我們向上滾動界面,CollapsingToolbarLayout收縮。當AppBarLayout完全摺疊的時候視頻av號隱藏,顯示出來一個小電視圖標和“立即播放”,點擊則使AppBarLayout完全展開,CollapsingToolbarLayout子佈局由ImageView切換爲視頻彈幕播放器。

額...彈幕播放器...

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

來我們先看修改完成的佈局。

<?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/coordinatorLayout"
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="@dimen/app_bar_height"
    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:contentScrim="?attr/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:src="@drawable/diqiu"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.7"
            android:fitsSystemWindows="true"/>

        <!--視頻及彈幕控件-->
        <FrameLayout
            android:id="@+id/video_danmu"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_collapseMode="parallax"
            app:layout_collapseParallaxMultiplier="0.7"
            android:fitsSystemWindows="true"
            android:visibility="gone">
            <VideoView
                android:id="@+id/videoview"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

            <!--嗶哩嗶哩開源的彈幕控件-->
            <master.flame.danmaku.ui.widget.DanmakuView
                android:id="@+id/danmaku"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </FrameLayout>

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

            <!--自定義帶圖片的立即播放按鈕-->
            <android.support.v7.widget.ButtonBarLayout
                android:id="@+id/playButton"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:gravity="center"
                android:visibility="gone">
                <ImageView
                    android:layout_width="wrap_content"
                    android:layout_height="match_parent"
                    android:layout_gravity="center_horizontal"
                    android:src="@mipmap/ic_play_circle_filled_white_48dp"/>

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#ffffff"
                    android:text="立即播放"
                    android:layout_gravity="center_vertical"
                   />
            </android.support.v7.widget.ButtonBarLayout>

        </android.support.v7.widget.Toolbar>
    </android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>

<include layout="@layout/content_scrolling" />

<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"
    android:src="@mipmap/ic_play_circle_filled_white_48dp"
    app:layout_anchor="@id/app_bar"
    app:layout_anchorGravity="bottom|end" />

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

我把colorPrimary的色值修改成了B站的“少女粉”,播放的圖標是從網上找的。

<color name="colorPrimary">#FA7199</color>

因爲我們要實現沉浸式狀態欄,所以就需要先把整個activity設置成狀態欄透明模式。然後在佈局文件中,把CollapsingToolbarLayout裏要實現沉浸式的控件設置上android:fitsSystemWindows="true",如果沒有設置,則子佈局會位於狀態欄下方,未延伸至狀態欄。

佈局並不算複雜,接下來先實現無彈幕播放時的功能,。

我們需要監聽CollapsingToolbarLayout的摺疊、展開狀態。唉我去,官方並沒有提供現成的方法(⊙_⊙?)。

查看源碼,可以看到CollapsingToolbarLayout是通過實現AppBarLayout的OnOffsetChangedListener接口,根據AppBarLayout的偏移來實現子佈局和title的視差移動以及ContentScrim和StatusBarScrim的顯示。那麼我們也可以通過調用AppBarLayout的addOnOffsetChangedListener方法監聽AppBarLayout的位移,判斷CollapsingToolbarLayout的狀態。

先寫一個枚舉定義出CollapsingToolbarLayout展開、摺疊、中間,這三種狀態。

 private CollapsingToolbarLayoutState state;

 private enum CollapsingToolbarLayoutState {
    EXPANDED,
    COLLAPSED,
    INTERNEDIATE
}

接下來對AppBarLayout進行監聽,判斷CollapsingToolbarLayout的狀態並實現相應的邏輯。

爲了讓大家對狀態看着更直觀,我在修改狀態值的時候把title一起進行了修改。

使用CollapsingToolbarLayout的時候要注意,在完成CollapsingToolbarLayout設置之後再調用Toolbar的setTitle()等方法將沒有效果,我們需要改爲調用CollapsingToolbarLayout的setTitle()等方法來對工具欄進行修改。(具體原因各位親去看下CollapsingToolbarLayout源碼就知道了 ( ˙-˙ ) )

    AppBarLayout  app_bar=(AppBarLayout)findViewById(R.id.app_bar);
    app_bar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {

            if (verticalOffset == 0) {
                if (state != CollapsingToolbarLayoutState.EXPANDED) {
                    state = CollapsingToolbarLayoutState.EXPANDED;//修改狀態標記爲展開
                    collapsingToolbarLayout.setTitle("EXPANDED");//設置title爲EXPANDED
                }
            } else if (Math.abs(verticalOffset) >= appBarLayout.getTotalScrollRange()) {
                if (state != CollapsingToolbarLayoutState.COLLAPSED) {
                    collapsingToolbarLayout.setTitle("");//設置title不顯示
                    playButton.setVisibility(View.VISIBLE);//隱藏播放按鈕
                    state = CollapsingToolbarLayoutState.COLLAPSED;//修改狀態標記爲摺疊
                }
            } else {
                if (state != CollapsingToolbarLayoutState.INTERNEDIATE) {
                    if(state == CollapsingToolbarLayoutState.COLLAPSED){
                        playButton.setVisibility(View.GONE);//由摺疊變爲中間狀態時隱藏播放按鈕
                    }
                    collapsingToolbarLayout.setTitle("INTERNEDIATE");//設置title爲INTERNEDIATE
                    state = CollapsingToolbarLayoutState.INTERNEDIATE;//修改狀態標記爲中間
                }
            }
        }
    });

然後對播放按鈕設置監聽,點擊則調用AppBarLayout的setExpanded(true)方法使工具欄展開。

strip
CollapsingToolbarLayout狀態監聽演示.gif

嗶哩嗶哩客戶端的title是固定不動的,可以調用CollapsingToolbarLayout的setTitleEnabled(false)方法實現。

視頻播放時,調用 NestedScrollView的setNestedScrollingEnabled(false)方法可以使AppBarLayout不響應滾動事件。

細心的朋友可能發現了嗶哩嗶哩客戶端爲了避免視頻封面圖片顏色過淺影響狀態欄信息的顯示,加了一個漸變的不透明層。

實現漸變遮罩層很簡單。先在res/drawable文件夾下新建了一個名爲gradient的xml文件,其中代碼如下:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">

    <gradient
        android:startColor="#33000000"
        android:endColor="#00000000"
        android:angle="270" />

</shape>

shape節點中,可以通過android:shape來設置形狀,默認是矩形。gradient節點中angle的值270是從上到下,0是從左到右,90是從下到上。起始顏色#33000000是20%不透明度的黑色,#00000000表示全透明。

然後在CollapsingToolbarLayout裏的ImageView代碼下面加上一個自定義view,背景設置爲上面的漸變效果。

<View
   android:layout_width="match_parent"
   android:layout_height="40dp"
   android:background="@drawable/gradient"
   android:fitsSystemWindows="true"
/>

一般狀態欄的高度大概在20dp左右,我爲了讓漸變效果比較自然,並且不過多影響圖(mei)片(zi),把高度設置成了40dp。(狀態欄能看清了,妹子臉也沒黑,挺好 (๑• . •๑) )

1240
有無漸變遮罩層的對比.jpg

我省略了彈幕播放的相關實現,接下來只要在播放按鈕監聽中寫出封面圖片的隱藏、視頻和彈幕彈幕控件的顯示初始化及播放邏輯,在AppBarLayout的三種狀態監聽中根據是否視頻在播放寫出其他相應邏輯就好了,感興趣的朋友可以下載嗶哩嗶哩的“烈焰彈幕使”源碼DanmakuFlameMaster玩玩。

B站點擊追番或投硬幣後會出現一個類似Snackbar的提示控件,可以通過我上一篇文章沒時間解釋了,快使用Snackbar!——Android Snackbar花式使用指南來實現,歡迎感興趣的朋友去看看。

strip
模仿嗶哩嗶哩視頻詳情頁.gif

真的不是我懶得上代碼了,真的…(基友:趕緊的,開黑了。 我:等等我,馬上來!\(≧▽≦)/)

三.CollapsingToolbarLayout與TabLayout

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

strip
CollapsingToolbarLayout與TabLayout.gif

來看下CollapsingToolbarLayout裏的代碼

<?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>

TabLayout沒有設置app:layout_collapseMode,在CollapsingToolbarLayout收縮時就不會消失。

CollapsingToolbarLayout收縮時的高度是Toolbar的高度,所以我們需要把Toolbar的高度增加,給TabLayout留出位置,這樣收縮後TabLayout就不會和Toolbar重疊。

Toolbar的高度增加,title會相應下移。android:gravity="top"方法使Toolbar的title位於Toolbar的上方,然後通過app:titleMarginTop調整下title距頂部高度,這樣Toolbar就和原來顯示的一樣了。


CollapsingToolbarLayout還可以和Palette搭配使用,但是我感覺在實際使用中有些坑,因爲CollapsingToolbarLayout中的圖片不確定,Palette從圖片中獲取到的色彩很可能不是你想要的。

感興趣的朋友可以自己查下Palette的用法。

就是這些。 []~( ̄▽ ̄)~*

發佈了21 篇原創文章 · 獲贊 11 · 訪問量 11萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章