在上篇文章中,和大家一起聊了聊AppBarLayout和CoordinatorLayout兩個新控件,以及CoordinatorLayout與FloatingActionButton、Snackbar的使用注意事項。至此,Android Material Design系列的學習已進行到第五篇,大家可以點擊以下鏈接查看之前的文章:
- Android TabLayout 分分鐘打造一個滑動標簽頁
- Android 一文告訴你到底是用Dialog,Snackbar,還是Toast
- Android FloatingActionButton 重要的操作不要太多,一個就好
- Android 初識AppBarLayout 和 CoordinatorLayout
本文繼續以案例的形式學習CoordinatorLayout的使用,配合者為AppBarLayout。文中會介紹一些這種搭配使用的案例下可能出現的問題以及解決方案,目的還是一句話,將我所知的分享出來,讓正在摸索的你少走一些彎路。老路子,先看一下本文要實現的效果圖:
簡單介紹下,這種設計經常會出現在各個app中,作為主頁顯示,其中主要包含了三個效果:
側拉導航菜單,這個是用v7包中的控件DrawerLayout實現的,不是本文重點,就不作過多說明了;
FAB與Snackbar的協調交互,在上篇文章中已經介紹的很詳細了,大家可以另行查看;
列表向上滑動,Toolbar向上隱藏,TabLayout固定于頂部,給內容區域留下更多的展示空間,本文著重講述這種實現。
知識點比較零碎,還是配合著代碼講解比較容易,思路也會比較清晰一些,主要布局文件的實現:
<?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/dl_root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<include layout="@layout/include_toolbar" />
<android.support.design.widget.TabLayout
android:id="@+id/tl_indicator"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="@+id/vp_content"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab_add"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/dp_16"
android:onClick="onClickFab"
android:src="@mipmap/ic_toolbar_add"
app:backgroundTint="@color/fab_ripple"
app:layout_anchor="@id/vp_content"
app:layout_anchorGravity="bottom|right|end" />
</android.support.design.widget.CoordinatorLayout>
<LinearLayout
android:id="@+id/ll_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:orientation="vertical"
android:gravity="center"
android:background="@color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Menu Content"
android:textColor="@color/black"
android:textSize="@dimen/sp_16"/>
</LinearLayout>
</android.support.v4.widget.DrawerLayout>
Android Developer官網中在介紹CoordinatorLayout時有這么一段話:
CoordinatorLayout is intended for two primary use cases:
- As a top-level application decor or chrome layout
- As a container for a specific interaction with one or more child views
也就是說CoordinatorLayout主要有兩種使用場景,在我理解過來就是,作為Activity布局中的最外層容器和作為指定交互行為的多個子控件的父容器來使用。而本文中的案例就屬于第二種。
要實現這種滑動交互效果,必須要滿足這么幾點:
CoordinatorLayout作為容器布局,來協調children view之間的交互行為;
使用AppBarLayout并設置其內部需要移出屏幕的View的scrollFlags屬性,在這個例子中也就是給Toolbar設置。由于Toolbar是通用布局代碼,這里我用了include標簽,其包含的布局代碼為:
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tb_toolbar"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_56"
app:titleTextColor="@color/white"
app:title="@string/app_name"
app:theme="@style/OverFlowMenuTheme"
app:popupTheme="@style/AppTheme"
android:background="@color/blue"
app:layout_scrollFlags="scroll|enterAlways"/>
可以看到,我添加了app:layout_scrollFlags
這個屬性,并將其值設為scroll|enterAlways
,scroll
表示滑動型控件向上滑動時toolbar將移出屏幕,enterAlways
表示向下滑動時toolbar將重新進入屏幕。由于TabLayout不需要移出屏幕,所以這里就不需要給它設置這個屬性了。需要注意的是:不要將app:layout_scrollFlags
屬性單獨設置子include標簽里,而是要放在include所加載的layout布局中,否則這個scrollFlags將失去作用,這與include標簽的使用有關。
- 一個特殊的滑動型控件并設置
layout_behavior
屬性,這里用的是ViewPager。注意,layout_behavior
的屬性值用的是系統定義好的固定字符串@string/appbar_scrolling_view_behavior
,大家感興趣的自己去翻閱源碼看看,后續介紹behavior時,我再仔細講解。
對于第三點,這里拿出來單獨強調一下,有沒有發現滑動型控件前我用了“特殊”兩個字來修飾!CoordinatorLayout之所以能夠協調Children View之間的交互行為,主要就是依賴于NestedScrolling
這個東西,這里涉及到兩個接口類NestedScrollingParent
和NestedScrollingChild
。CoordinatorLayout實現了前者,而CoordinatorLayout的Children核心之一,滑動型控件,實現了后者,所以才能夠做出這個交互行為。關于NestedScrolling
,后續再寫文單獨介紹。所以,這個特殊的滑動型控件必須是實現了NestedScrollingChild
接口的控件,比如v7包中的RecyclerView,看一下它的定義就知道了:
public class RecyclerView extends ViewGroup implements ScrollingView, NestedScrollingChild
故,本文中的ViewPager里面的列表控件必須是RecyclerView。如果你僅僅是簡單地使用ListView,還是達不到這樣的效果。聰明如你,肯定又看出了我的措辭,對的,我又用了一個詞:“簡單地使用”,那就說明其實稍作處理,復雜點使用,也能夠使用ListView的。
在API Level 21及更高版本,為了支持NestedScrolling
,所有控件的基類View對外新增了一個方法setNestedScrollingEnabled(boolean enabled)
,所以,我們可以對ListView稍作處理,就能在Android L及以上版本的系統中使用了:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
listView.setNestedScrollingEnabled(true);
}
或者借助 v4 包中的 ViewCompat
類為 ListView 添加設置,避免版本判斷:
ViewCompat.setNestedScrollingEnabled(listView, true);
但是,這兩種方法均只支持 Android Lollipop 及更高版本,在 pre-lollipop 上是沒有效果的。其實,ListView真的已經過氣了,我們應該全方位掌握RecyclerView的使用,就像Android Studio取代Eclipse一樣。
其他的代碼就很簡單了,就是給DrawerLayout設置ActionBarDrawerToggle
,就是圖中ToolBar左側的菜單按鈕。然后用Fragment填充ViewPager,這里就不貼代碼了,工程Demo都在GitHub上,大家可以自己下載參考。
其實這個案例的實現還是蠻簡單的,文中零零碎碎地講述了很多使用過程中的細節技巧,幫助大家解決實際問題。下篇文章繼續使用案例,介紹CoordinatorLayout的使用方法,同時引入另一個控件的使用,歡迎關注!
示例源碼
我在GitHub上建立了一個Repository,用來存放整個Android Material Design系列控件的學習案例,會伴隨著文章逐漸更新完善,歡迎大家補充交流,Star地址: