最詳細(xì)的 NavigationDrawer 開(kāi)發(fā)實(shí)踐總結(jié)
繼前面寫(xiě)的兩篇文章之后(有問(wèn)題歡迎反饋哦):
- Android開(kāi)發(fā):Translucent System Bar 的最佳實(shí)踐
- Android開(kāi)發(fā):最詳細(xì)的 Toolbar 開(kāi)發(fā)實(shí)踐總結(jié)
接著來(lái)寫(xiě)寫(xiě)Android系統(tǒng)UI新特性拧簸,本文是我對(duì)最近開(kāi)發(fā)過(guò)程中應(yīng)用 NavigationDrawer 特性的詳細(xì)總結(jié)哲戚。本文涉及到的所有代碼實(shí)現(xiàn)細(xì)節(jié)廷粒,會(huì)在文末附上源碼地址段标。有問(wèn)題歡迎在下方留言討論 。
NavigationDrawer 簡(jiǎn)介
NavigationDrawer 是 Google 在 Material Design 中推出的一種側(cè)滑導(dǎo)航欄設(shè)計(jì)風(fēng)格。說(shuō)起來(lái)可能很抽象,我們直接來(lái)看看 網(wǎng)易云音樂(lè) 的側(cè)滑導(dǎo)航欄效果
Google 為了支持這樣的導(dǎo)航效果,推出一個(gè)新控件 —— DrawerLayout 题涨。而在 DrawerLayout 沒(méi)誕生之前,需求中需要實(shí)現(xiàn)側(cè)滑導(dǎo)航效果時(shí)总滩,我們必然會(huì)選擇去選擇一些成熟的第三方開(kāi)源庫(kù)(如最有名的 SlidingMenu)來(lái)完成開(kāi)發(fā) 纲堵。效果上,普遍都像 手Q 那樣:
在對(duì)比過(guò) DrawerLayout 和 SlidingMenu 的實(shí)現(xiàn)效果后闰渔,基于以下的幾點(diǎn)席函,我認(rèn)為完全可以在開(kāi)發(fā)中使用 DrawerLayout 取代以前的 SlidingMenu:
- 從動(dòng)畫(huà)效果上看,你會(huì)發(fā)現(xiàn)兩者僅僅是在移動(dòng)的效果上有些差別外冈涧,其他地方并沒(méi)有太大的差異
- 在交互效果上茂附,我認(rèn)為這兩者都差不多的,就算你把 網(wǎng)易云音樂(lè) 的效果套到了 手Q 上督弓,也不會(huì)影響到用戶的交互
- DrawerLayout 用起來(lái)比 SlidingMenu 更簡(jiǎn)單营曼,代碼量更少(往下看就知道了)
- DrawerLayout 是向下兼容的,所以不會(huì)存在低版本兼容性問(wèn)題
- Google 親兒子愚隧,沒(méi)理由不支持暗仝濉!?裉痢B济骸!\窈妈踊!
到這里,要是你還沒(méi)有引入 DrawerLayout 開(kāi)發(fā)的沖動(dòng)泪漂,請(qǐng)繼續(xù)聽(tīng)我為你好好安利一番廊营。
初識(shí) DrawerLayout
一般情況下,在 DrawerLayout 布局下只會(huì)存在兩個(gè)子布局窖梁,一個(gè) 內(nèi)容布局 和 一個(gè) 側(cè)滑菜單布局赘风,這兩個(gè)布局關(guān)鍵在于 android:layout_gravity 屬性的設(shè)置夹囚。如果你想把其中一個(gè)子布局設(shè)置成為左側(cè)滑菜單纵刘,只需要設(shè)置 android:layout_gravity="start" 即可(也可以是 left,右側(cè)滑則為 end 或 right )荸哟,而沒(méi)有設(shè)置的布局則自然成為 內(nèi)容布局 假哎。那么瞬捕,使用 DrawerLayout 到底有多簡(jiǎn)單呢,我們先直接看看下面的布局文件
layout/activity_simple_drawer.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v4.widget.DrawerLayout
android:id="@+id/simple_navigation_drawer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!--內(nèi)容視圖-->
<include
android:id="@+id/tv_content"
layout="@layout/drawer_content_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<!--左側(cè)滑菜單欄-->
<include
layout="@layout/drawer_menu_layout"
android:layout_width="250dp"
android:layout_height="match_parent"
android:layout_gravity="start" />
<!--右側(cè)滑菜單欄-->
<include
layout="@layout/drawer_menu_layout"
android:layout_width="250dp"
android:layout_height="match_parent"
android:layout_gravity="end" />
</android.support.v4.widget.DrawerLayout>
</RelativeLayout>
到此舵抹,你在 Activity 里面什么都不用做肪虎,就已經(jīng)完成了下面?zhèn)然Ч膶?shí)現(xiàn)了,簡(jiǎn)單到害怕有木有惧蛹。
在欣賞著 DrawerLayout 簡(jiǎn)單方便的同時(shí)扇救,Google 也為我們提供了 DrawerLayout 很多常用的API,其中包括:打開(kāi)或關(guān)閉側(cè)滑欄香嗓、控制側(cè)滑欄的方向迅腔、設(shè)置滑動(dòng)時(shí)漸變的陰影顏色和監(jiān)聽(tīng)滑動(dòng)事件等。
具體詳細(xì)代碼請(qǐng)參加工程中的 SimpleDrawerActivity靠娱,此處就不貼代碼了沧烈。還有一處 DrawerLayout 使用的小細(xì)節(jié)需要溫馨提醒一下,有一次像云,我手誤把 DrawerLayout 的 android:layout_width 設(shè)置成 wrap_content锌雀,就出現(xiàn)下面的異常了
遇到過(guò)相同情況的童鞋,只需要把 android:layout_width 設(shè)置成 match_parent 即可迅诬。
再識(shí) NavigationView
在 Google 推出 NavigationDrawer 設(shè)計(jì)中腋逆,NavigationView 和 DrawerLayout 是官方推薦的最佳組合。在使用 NavigationView 前侈贷,因?yàn)樗窃?Material Design 的兼容包中闲礼,所以需要先在 build.gradle 中引入
compile 'com.android.support:design:23.1.1'
這里因?yàn)槲夜こ膛渲玫?compileSdkVersion 是 23 ,所以需要引入 com.android.support:design:23.x.x 的版本铐维。需要吐槽的是柬泽,這里如果你引入了 com.android.support:design:23.1.0 ,工程運(yùn)行后 NavigationView 會(huì)報(bào)一個(gè) android.view.InflateException:xxxxxx 的錯(cuò)誤(又是一個(gè)大坑)嫁蛇。
接下來(lái)簡(jiǎn)單的介紹一下 NavigationView 的使用锨并,我們繼續(xù)看看幾個(gè)相關(guān)布局文件 layout/activity_simple_navigation_drawer.xml、layout/navigation_drawer_header.xml睬棚、menu/navigation_drawer_menu.xml 和 實(shí)現(xiàn)效果:
layout/activity_simple_navigation_drawer.xml
<?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/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:text="NavigationDrawerContent" />
</LinearLayout>
<android.support.design.widget.NavigationView
android:id="@+id/navigation_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:headerLayout="@layout/navigation_drawer_header"
app:menu="@menu/navigation_drawer_menu" />
</android.support.v4.widget.DrawerLayout>
layout/navigation_drawer_header.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="250dp"
android:background="@color/color_512da8">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_margin="10dp"
android:text="HeaderLayout"
android:textColor="@android:color/white"
android:textSize="18sp" />
</RelativeLayout>
menu/navigation_drawer_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<group android:checkableBehavior="single">
<item
android:id="@+id/item_green"
android:icon="@mipmap/green"
android:title="Green" />
<item
android:id="@+id/item_blue"
android:icon="@mipmap/blue"
android:title="Blue" />
<item
android:id="@+id/item_pink"
android:icon="@mipmap/pink"
android:title="Pink" />
</group>
<item android:title="SubItems">
<menu>
<item
android:id="@+id/subitem_01"
android:icon="@mipmap/ic_launcher"
android:title="SubItem01" />
<item
android:id="@+id/subitem_02"
android:icon="@mipmap/ic_launcher"
android:title="SubItem02" />
<item
android:id="@+id/subitem_03"
android:icon="@mipmap/ic_launcher"
android:title="SubItem03" />
</menu>
</item>
<item android:title="SubItems">
<menu>
<item
android:id="@+id/subitem_04"
android:icon="@mipmap/ic_launcher"
android:title="SubItem04" />
<item
android:id="@+id/subitem_05"
android:icon="@mipmap/ic_launcher"
android:title="SubItem05" />
<item
android:id="@+id/subitem_06"
android:icon="@mipmap/ic_launcher"
android:title="SubItem06" />
</menu>
</item>
</menu>
最終得到下面的效果
總的來(lái)說(shuō)第煮,NavigationView 比較關(guān)鍵的屬性就只有 app:headerLayout 和 app:menu ,它們分別對(duì)應(yīng)效果圖中頂部的 紫色區(qū)域(layout/navigation_drawer_header.xml) 和 下方的 填充菜單項(xiàng)(menu/navigation_drawer_menu.xml)抑党。其實(shí)是用起來(lái)也和 DrawerLayout 一樣包警,非常簡(jiǎn)單。
不實(shí)用的 NavigationView
其實(shí)談到 NavigationView底靠,個(gè)人認(rèn)為它設(shè)計(jì)并不實(shí)用害晦,而且是比較呆板的。最直接的一點(diǎn)是暑中,它的菜單圖標(biāo)
第一次運(yùn)行代碼的時(shí)候壹瘟,把我五顏六色的圖標(biāo)居然跑出來(lái)這效果鲫剿,差點(diǎn)沒(méi)一口水噴在屏幕上。好在代碼中可以調(diào)用下面這個(gè)API
mNavigationView.setItemIconTintList(null);//設(shè)置菜單圖標(biāo)恢復(fù)本來(lái)的顏色
還原菜單圖標(biāo)廬山真面目稻轨。(著實(shí)看不懂 Google 的設(shè)計(jì)了...)
其次灵莲,是關(guān)于菜單相中圖標(biāo)大小和文字間距之類(lèi)的設(shè)置,從 Google 的設(shè)計(jì)文檔來(lái)看殴俱,
NavigationView 基本已經(jīng)規(guī)定設(shè)置好了大小距離政冻,留給我們可以改動(dòng)的空間并不多。如果你想調(diào)整一下菜單的布局寬高之類(lèi)的线欲,基本是不可能的了(即使可能赠幕,也估計(jì)非常蛋疼)。所以询筏,目前我基本還沒(méi)見(jiàn)過(guò)國(guó)內(nèi)哪個(gè) app 是直接使用了 NavigationView 來(lái)做導(dǎo)航(如果有的話榕堰,歡迎告知一下)。
以上關(guān)于 NavigationView 不實(shí)用嫌套,僅是本人的一些看法逆屡,如果你有不同看法,歡迎留言討論踱讨。為了加深一下 NavigationDrawer 設(shè)計(jì)的實(shí)踐魏蔗,下面來(lái)大致的模仿實(shí)現(xiàn)網(wǎng)易云音樂(lè)的導(dǎo)航效果。
仿網(wǎng)易云音樂(lè)的 NavigationDrawer 實(shí)現(xiàn)
先來(lái)看看網(wǎng)易云音樂(lè)的效果
主要就是一個(gè)線性布局的菜單并結(jié)合了 Translucent System Bar 的特性(還不知道的童鞋請(qǐng)看我前面寫(xiě)的文章哈)痹筛,下面就直接看看大致實(shí)現(xiàn)的布局文件 :
layout/activity_cloud_music.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/drawer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/color_cd3e3a">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="65dp"
android:background="@color/color_cd3e3a"
android:gravity="center"
android:text="網(wǎng)易云音樂(lè)"
android:textColor="@android:color/white"
android:textSize="18sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"
android:orientation="vertical">
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/navigation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="@android:color/white"
android:fitsSystemWindows="true"
android:orientation="vertical">
<ImageView
android:layout_width="match_parent"
android:layout_height="180dp"
android:scaleType="centerCrop"
android:src="@mipmap/topinfo_ban_bg" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:src="@mipmap/topmenu_icn_msg" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="我的消息"
android:textColor="@android:color/black"
android:textSize="15sp" />
</LinearLayout>
...
...
...
</LinearLayout>
</android.support.v4.widget.DrawerLayout>
最終即可實(shí)現(xiàn)類(lèi)似網(wǎng)易云音樂(lè)的效果莺治。
彩蛋
彩蛋一:左上角的導(dǎo)航動(dòng)畫(huà)效果實(shí)現(xiàn)
經(jīng)常會(huì)看有些 app 的左上角有這些帶感的導(dǎo)航動(dòng)畫(huà),之前想要引入這種效果帚稠,都是來(lái)自第三方的開(kāi)源代碼谣旁,諸如下面兩個(gè)比較有名的:
而現(xiàn)在再也不需要了,Google 推出的 ActionBarDrawerToggle 也能實(shí)現(xiàn)這樣的效果了滋早,具體查看我在 NavigationDrawerAnimationActivity 中的實(shí)現(xiàn)代碼
如果你對(duì)上面這種動(dòng)畫(huà)榄审,效果不滿意,也可以考慮一下 material-menu 的另一種實(shí)現(xiàn)效果杆麸。
彩蛋二:比 NavigationView 更好的選擇
前面提到 NavigationView 的不實(shí)用性搁进,如果你真的要實(shí)現(xiàn) NavigationView那樣的效果,又渴望比較高的自由度昔头。這個(gè)功能強(qiáng)大且自由度很高的開(kāi)源庫(kù) MaterialDrawer 應(yīng)該是個(gè)很不錯(cuò)的選擇饼问。
總結(jié)
到此,對(duì)于 NavigationDrawer 的實(shí)踐總結(jié)基本結(jié)束揭斧。整體給我的感覺(jué)是莱革,自從 Material Design 設(shè)計(jì)開(kāi)始推出后,Google 推出的這些新控件使用起來(lái)更加簡(jiǎn)單,這能讓我們更好的把精力放在編寫(xiě)業(yè)務(wù)代碼上驮吱。很多以前需要借助第三方開(kāi)源庫(kù)才能實(shí)現(xiàn)的效果,現(xiàn)在已經(jīng)慢慢的不需要了萧吠。當(dāng)然左冬,我們依舊可以去深入的學(xué)習(xí)這些優(yōu)秀開(kāi)源代碼,沉淀到更多的干貨纸型。這樣拇砰,小菜也就慢慢成為大牛了。
分享即美德狰腌,源代碼請(qǐng)看:https://github.com/D-clock/AndroidSystemUiTraining 除破,本篇的主要實(shí)現(xiàn)代碼如下紅圈所示
歡迎關(guān)注我的簡(jiǎn)書(shū),以及:
- 知乎:https://www.zhihu.com/people/d_clock
- 知乎專(zhuān)欄(技術(shù)視界):https://zhuanlan.zhihu.com/coderclock
- 知乎專(zhuān)欄(閱讀視界):https://zhuanlan.zhihu.com/readerclock
- 個(gè)人博客:http://blog.coderclock.com/
-
微信公眾號(hào):