Android CollapsingToolbarLayout使用介紹
我非常喜歡Material Design裏折疊工具欄的效果,bilibili Android客戶端視頻詳情頁就是采用的這種設計。這篇文章的第二部分我們就通過簡單的模仿bilibili視頻詳情頁的實現來了解下CollapsingToolbarLayout的使用。文章的第三部分介紹了CollapsingToolbarLayout與TabLayout的組合使用。
有基礎的朋友可以直接跳過第一部分。
一、相關基礎屬性介紹
Android studio中有一個Activity模板叫ScrollingActivity,它實現的就是簡單的可折疊工具欄,我們將此模板添加到項目中。
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屬性):
- scroll:將此布局和滾動時間關聯。這個標識要設置在其他標識之前,沒有這個標識則布局不會滾動且其他標識設置無效。
- enterAlways:任何向下滾動操作都會使此布局可見。這個標識通常被稱為“快速返回”模式。
- enterAlwaysCollapsed:假設你定義了一個最小高度(minHeight)同時enterAlways也定義了,那麽view將在到達這個最小高度的時候開始顯示,並且從這個時候開始慢慢展開,當滾動到頂部的時候展開完。
- exitUntilCollapsed:當你定義了一個minHeight,此布局將在滾動到達這個最小高度的時候折疊。
- snap:當一個滾動事件結束,如果視圖是部分可見的,那麽它將被滾動到收縮或展開。例如,如果視圖只有底部25%顯示,它將折疊。相反,如果它的底部75%可見,那麽它將完全展開。
CollapsingToolbarLayout可以通過app:contentScrim設置折疊時工具欄布局的顏色,通過app:statusBarScrim設置折疊時狀態欄的顏色。默認contentScrim是colorPrimary的色值,statusBarScrim是colorPrimaryDark的色值。這個後面會用到。
CollapsingToolbarLayout的子布局有3種折疊模式(Toolbar中設置的app:layout_collapseMode)
- off:這個是默認屬性,布局將正常顯示,沒有折疊的行為。
- pin:CollapsingToolbarLayout折疊後,此布局將固定在頂部。
- parallax:CollapsingToolbarLayout折疊時,此布局也會有視差折疊效果。
當CollapsingToolbarLayout的子布局設置了parallax模式時,我們還可以通過app:layout_collapseParallaxMultiplier設置視差滾動因子,值為:0~1。
FloatingActionButton這個控件通過app:layout_anchor這個設置錨定在了AppBarLayout下方。FloatingActionButton源碼中有一個Behavior方法,當AppBarLayout收縮時,FloatingActionButton就會跟著做出相應變化。關於CoordinatorLayout和Behavior,我下一篇文章會和大家一起學習。
這一堆屬性看著有點煩,大家可以新建一個ScrollingActivity模板去實驗一下玩玩。
二、模仿bilibili客戶端視頻詳情頁
我們先對原界面分析一下。
嗶哩嗶哩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)方法使工具欄展開。
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。(狀態欄能看清了,妹子臉也沒黑,挺好 (?• . •?) )
有無漸變遮罩層的對比.jpg
我省略了彈幕播放的相關實現,接下來只要在播放按鈕監聽中寫出封面圖片的隱藏、視頻和彈幕彈幕控件的顯示初始化及播放邏輯,在AppBarLayout的三種狀態監聽中根據是否視頻在播放寫出其他相應邏輯就好了,感興趣的朋友可以下載嗶哩嗶哩的“烈焰彈幕使”源碼DanmakuFlameMaster玩玩。
B站點擊追番或投硬幣後會出現一個類似Snackbar的提示控件,可以通過我上一篇文章沒時間解釋了,快使用Snackbar!——Android Snackbar花式使用指南來實現,歡迎感興趣的朋友去看看。
模仿嗶哩嗶哩視頻詳情頁.gif
真的不是我懶得上代碼了,真的…(基友:趕緊的,開黑了。 我:等等我,馬上來!\(≧▽≦)/)
三.CollapsingToolbarLayout與TabLayout
CollapsingToolbarLayout與TabLayout組合使用的效果也不錯。
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的用法。
就是這些。 []~( ̄▽ ̄)~*
Android CollapsingToolbarLayout使用介紹