最詳細的 NavigationDrawer 開發實踐總結

2021-03-02 Android程式設計師

原文:http://www.jianshu.com/p/c8cbeb7ea43a

作者:D_clock

今天投稿的D_clock同學,不久前曾給我投稿過《最詳細的 Toolbar 開發實踐總結》一文,發布後大家反饋很好,最近又奮筆疾書,熬出此文,個人感覺D_clock同學寫起這類文章是越來越6,與他交流後得知:他雖寫博客文章不久,但提筆之前,也是用心觀察了很多優秀博文的寫法,其文章很多細節都花了不少心思打磨,這份用心也非常值得稱讚。

繼前面寫的兩篇文章之後(有問題歡迎反饋哦):

Android開發:Translucent System Bar 的最佳實踐

Android開發:最詳細的 Toolbar 開發實踐總結

接著來寫寫Android系統UI新特性,本文是我對最近開發過程中應用 NavigationDrawer 特性的詳細總結。本文涉及到的所有代碼實現細節,會在文末附上源碼地址。有問題歡迎在下方留言討論

NavigationDrawer 簡介

NavigationDrawer 是 Google 在 Material Design 中推出的一種側滑導航欄設計風格。說起來可能很抽象,我們直接來看看 網易雲音樂 的側滑導航欄效果

Google 為了支持這樣的導航效果,推出一個新控制項 —— DrawerLayout 。而在 DrawerLayout 沒誕生之前,需求中需要實現側滑導航效果時,我們必然會選擇去選擇一些成熟的第三方開源庫(如最有名的 SlidingMenu)來完成開發 。效果上,普遍都像 手Q 那樣:

在對比過 DrawerLayoutSlidingMenu 的實現效果後,基於以下的幾點,我認為完全可以在開發中使用 DrawerLayout 取代以前的 SlidingMenu

從動畫效果上看,你會發現兩者僅僅是在移動的效果上有些差別外,其他地方並沒有太大的差異

在交互效果上,我認為這兩者都差不多的,就算你把 網易雲音樂 的效果套到了 手Q 上,也不會影響到用戶的交互

DrawerLayout 用起來比 SlidingMenu 更簡單,代碼量更少(往下看就知道了)

DrawerLayout 是向下兼容的,所以不會存在低版本兼容性問題

Google 親兒子,沒理由不支持啊!!!!!!

到這裡,要是你還沒有引入 DrawerLayout 開發的衝動,請繼續聽我為你好好安利一番。

初識 DrawerLayout

一般情況下,在 DrawerLayout 布局下只會存在兩個子布局,一個 內容布局 和 一個 側滑菜單布局,這兩個布局關鍵在於 android:layout_gravity 屬性的設置。如果你想把其中一個子布局設置成為左側滑菜單,只需要設置 android:layout_gravity=」start」 即可(也可以是 left,右側滑則為 end 或 right ),而沒有設置的布局則自然成為 內容布局 。那麼,使用 DrawerLayout 到底有多簡單呢,我們先直接看看下面的布局文件
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">                <include            android:id="@+id/tv_content"            layout="@layout/drawer_content_layout"            android:layout_width="match_parent"            android:layout_height="match_parent" />                <include            layout="@layout/drawer_menu_layout"            android:layout_width="250dp"            android:layout_height="match_parent"            android:layout_gravity="start" />                <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 裡面什麼都不用做,就已經完成了下面側滑效果的實現了,簡單到害怕有木有。

在欣賞著 DrawerLayout 簡單方便的同時,Google 也為我們提供了 DrawerLayout 很多常用的API,其中包括:打開或關閉側滑欄、控制側滑欄的方向、設置滑動時漸變的陰影顏色和監聽滑動事件等。

具體詳細代碼請參加工程中的 SimpleDrawerActivity,此處就不貼代碼了。還有一處 DrawerLayout 使用的小細節需要溫馨提醒一下,有一次,我手誤把 DrawerLayoutandroid:layout_width 設置成 wrap_content,就出現下面的異常了

遇到過相同情況的童鞋,只需要把 android:layout_width 設置成 match_parent 即可。

再識 NavigationView

在 Google 推出 NavigationDrawer 設計中,NavigationViewDrawerLayout 是官方推薦的最佳組合。在使用 NavigationView 前,因為它是在 Material Design 的兼容包中,所以需要先在 build.gradle 中引入

compile 'com.android.support:design:23.1.1'

這裡因為我工程配置的 compileSdkVersion23 ,所以需要引入 com.android.support:design:23.x.x 的版本。需要吐槽的是,這裡如果你引入了 com.android.support:design:23.1.0 ,工程運行後 NavigationView 會報一個  android.view.InflateException:xxxxxx 的錯誤(又是一個大坑)。
接下來簡單的介紹一下 NavigationView 的使用,我們繼續看看幾個相關布局文件 layout/activity_simple_navigation_drawer.xmllayout/navigation_drawer_header.xmlmenu/navigation_drawer_menu.xml 和 實現效果:
layout/activity_simple_navigation_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayoutxmlns: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>

最終得到下面的效果

總的來說,NavigationView 比較關鍵的屬性就只有 app:headerLayoutapp:menu ,它們分別對應效果圖中頂部的 紫色區域(layout/navigation_drawer_header.xml) 和 下方的 填充菜單項(menu/navigation_drawer_menu.xml)。其實是用起來也和 DrawerLayout 一樣,非常簡單。

不實用的 NavigationView

其實談到 NavigationView,個人認為它設計並不實用,而且是比較呆板的。最直接的一點是,它的菜單圖標

第一次運行代碼的時候,把我五顏六色的圖標居然跑出來這效果,差點沒一口水噴在屏幕上。好在代碼中可以調用下面這個API

//設置菜單圖標恢復本來的顏色
mNavigationView.setItemIconTintList(null);

還原菜單圖標廬山真面目。(著實看不懂 Google 的設計了…)
其次,是關於菜單相中圖標大小和文字間距之類的設置,從 Google 的設計文檔來看,

NavigationView 基本已經規定設置好了大小距離,留給我們可以改動的空間並不多。如果你想調整一下菜單的布局寬高之類的,基本是不可能的了(即使可能,也估計非常蛋疼)。所以,目前我基本還沒見過國內哪個 app 是直接使用了 NavigationView 來做導航(如果有的話,歡迎告知一下)。

以上關於 NavigationView 不實用,僅是本人的一些看法,如果你有不同看法,歡迎留言討論。為了加深一下 NavigationDrawer 設計的實踐,下面來大致的模仿實現網易雲音樂的導航效果。

仿網易雲音樂的 NavigationDrawer 實現

先來看看網易雲音樂的效果

主要就是一個線性布局的菜單並結合了 Translucent System Bar 的特性(還不知道的童鞋請看我前面寫的文章哈),下面就直接看看大致實現的布局文件 :
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="網易雲音樂"            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>

最終即可實現類似網易雲音樂的效果。

彩蛋

彩蛋一:左上角的導航動畫效果實現

經常會看有些 app 的左上角有這些帶感的導航動畫,之前想要引入這種效果,都是來自第三方的開原始碼,諸如下面兩個比較有名的:

LDrawer (https://github.com/keklikhasan/LDrawer)

android-ui (https://github.com/markushi/android-ui)

而現在再也不需要了,Google 推出的 ActionBarDrawerToggle 也能實現這樣的效果了,具體查看我在 NavigationDrawerAnimationActivity 中的實現代碼

如果你對上面這種動畫,效果不滿意,也可以考慮一下 material-menu 的另一種實現效果。

彩蛋二:比 NavigationView 更好的選擇
前面提到 NavigationView 的不實用性,如果你真的要實現 NavigationView那樣的效果,又渴望比較高的自由度。這個功能強大且自由度很高的開源庫 MaterialDrawer(https://github.com/mikepenz/MaterialDrawer) 應該是個很不錯的選擇。


總結

到此,對於 NavigationDrawer 的實踐總結基本結束。整體給我的感覺是,自從 Material Design 設計開始推出後,Google 推出的這些新控制項使用起來更加簡單,這能讓我們更好的把精力放在編寫業務代碼上。很多以前需要藉助第三方開源庫才能實現的效果,現在已經慢慢的不需要了。當然,我們依舊可以去深入的學習這些優秀開原始碼,沉澱到更多的乾貨。這樣,小菜也就慢慢成為大牛了。

分享即美德,原始碼請看:https://github.com/D-clock/AndroidSystemUiTraining ,本篇的主要實現代碼如下紅圈所示

如果喜歡這篇文章,記得點讚與分享給好友。

如果你還想了解更多Android開發最佳實踐、經驗分享、最好用的工具與服務,技術前沿,請長按下方二維碼或搜索微信公眾號:AndroidTrending關注我,我會繼續保持精品。

相關焦點

  • 最詳細的 Toolbar 開發實踐總結
    說起 Toolbar ,可能有很多開發的童鞋還比較陌生,沒關係,請接著往下看。,會發現 app logo 和 title、subtitle 的間距比較小,看起來不如 導航圖標 與 它們兩搭配美觀;Toolbar 和其他控制項一樣,很多屬性設置方法既支持代碼設置,也支持在xml中設置(這裡也是最最最最最坑爹的地方
  • 熟詞生義:「top drawer」不是指「最上面的抽屜」!
    大家好,今天我們要分享的表達是——top drawer, 它的含義不是指「最上面的抽屜」,其真實的含義是:top drawer 最優秀的 She brought together a team of top-drawer
  • 動畫實用教程 | 在屏幕上添加一個 Drawer
    當沒有足夠的空間來支持 tab 導航時,drawer 提供了另一個方便的選擇。添加一個 drawer3. 向 drawer 中添加內容4. 通過編程關閉 drawer1. 創建一個 Scaffold為了向應用中添加一個 Drawer,我們需要將其放在 Scaffold widget 中。
  • 「from the top drawer」別理解成「來自上面的抽屜」
    大家好,歡迎來的餅哥英語的頻道,今天我們分享一個非常有用且地道的表達——from top drawer, 這個短語的含義不是指「來自上面的抽屜」,其正確的含義是:from the top drawer 出身名門望族,來自上流社會
  • Material Design之 AppbarLayout 開發實踐總結
    前言前一篇文章是Material Design 系列的第一篇文章,講了Toolbar 的使用,《Material Design 之 Toolbar 開發實踐總結》,還沒看過的同學可以去看一下,這篇是Material Design
  • Node開發實踐總結——定時腳本的設計與實現
    來源:騰訊IMWeb前端團隊前言作為Node語言的初學者去實踐後端開發時,不僅僅有見獵心喜,也有一些忐忑,好在大家都很open,給予了很多建議和分享,到目前為止,也成功建立了三個基於Node.js + TypeScript + IMServer 1 的工程,也是時候將自己最近的學習過程進行總結,下面就以一個小小的開發任務為載體分享下我的成長過程
  • Node開發實踐總結-定時腳本的設計與實現
    前言作為Node語言的初學者去實踐後端開發時,不僅僅有見獵心喜,也有一些忐忑,好在大家都很open,給予了很多建議和分享,到目前為止,也成功建立了三個基於Node.js + TypeScript + IMServer   1   的工程,也是時候將自己最近的學習過程進行總結,下面就以一個小小的開發任務為載體分享下我的成長過程
  • 高中生物會考知識點總結【最詳細】
    高中生物會考知識點總結【最詳細】高中生物是理科生高考中必考的科目,高中生物複習時必背的小知識點有哪些,下面有途網小編給大家整理了高中生物會考知識點總結【最詳細】,希望對你有幫助。高中生物會考知識點總結【最詳細】1、糖類代謝、蛋白質代謝、脂類代謝的圖解參見課本。
  • 小程序開發實踐總結,重慶定製小程序開發
    我公司都不除外,大家全部前端開發精英團隊這大半年來大部分全是在開發小程序。從頭至尾也開發設計了四五個微信小程序了。總感覺要留有點什麼,既是紀錄那些年我們踩過的坑,也是期待大伙兒別再掉坑。繁雜部件的開發設計,省區市三級聯動選擇符的開發設計,獲得微信地址庫的詳細地址的編號和業務流程選用的省區市編號不一樣。網頁頁面途徑的等級,較大不可以超出10層。
  • Node.js 開發實踐總結-定時腳本的設計與實現
    作者@騰訊IMWeb前端團隊 | 地址@https://mp.weixin.qq.com/s/a_kIRvJUuICtw0CG7EFAtg前言作為Node語言的初學者去實踐後端開發時,不僅僅有見獵心喜,也有一些忐忑,好在大家都很open,給予了很多建議和分享,到目前為止,也成功建立了三個基於Node.js + TypeScript + IMServer 1 的工程,也是時候將自己最近的學習過程進行總結,下面就以一個小小的開發任務為載體分享下我的成長過程。
  • 高中文科生物會考知識點總結【最詳細】
    高中文科生物會考知識點總結【最詳細】 2019-04-23 19:55:38 來源:網絡資源
  • ...to launch last satellite for BeiDou navigation system in May
    The BDS is China's independently developed and operated global satellite navigation system.
  • 成功實踐敏捷開發的26條「軍規」
    【IT168 專稿】    如何才能成功實踐敏捷開發是一個課題。最近Keith Swenson一直在考慮這個問題,並最終總結出26條重要原則,以指導敏捷軟體開發團隊更好地工作。    原則1:第1個用例完全處理好後再開始處理第2個用例。打個比方說,好比「先上這道菜,再開始做下一道菜」。
  • 博物館研學旅行課程開發與實踐
    2017年12月,教育部辦公廳發布了《關於公布第一批全國中小學生研學實踐教育基地、營地名單的通知》,在率先公布的204個全國中小學研學實踐教育基地中,就有51家博物館(含科技館、科學中心、自然博物館、陳列館),還包括具有博物館、科技館性質的36個紀念館、4個科普中心。可見,博物館作為研學教育的重要力量,其教育功能將上升到新的高度。
  • 從零到一Hybrid APP開發實踐|StuQ小班課
    StuQ 邀請百度高級前端開發工程師謝鬱推出《Hybrid APP開發實踐》精品小班教學,僅有 50 席位,精品小班課區別於大眾在線視頻教學,課程設置具有如下特色
  • 一個開發人員的RHCA考試總結
    我想總結一下自己作為一個多年的開發人員的考試經歷。當初選擇軟體開發的時候,雖然做了很多年的大型金融行業項目,去過幾十個城市的股份制和商業銀行,帶領幾十個人的團隊開發過很多大中小銀行的網上銀行系統。但是有一點一直在我心裡不踏實,也就是對作業系統Linux不熟悉,經常被系統運維人員批評:連Linux都不熟悉! 怎麼當程式設計師?
  • 聚焦 | 小語經典課文微型課程的開發實踐
    >        ③同時以微型課程開發為基點進一步反思完善校本課程開發和實踐的模式和策略、方法,為校本研修的進一步優化和深入提供借鑑和幫助。實踐活動:相關語文實踐活動或語文綜合性學習建議及指導。        ②教師要充分閱讀學習小學語文經典課文微型課程教材,要深刻領會教材的開發目的和特色。這樣,在實際應用中才能做到有的放矢。        ③教師要向對待國家教材一樣,做出詳細有效的教學計劃,要保證教材應用的時間,要有合理的教學方法,並同時做到對學生教授有方,學生學有所獲。
  • Android架構之App組件化方案詳細實踐與總結
    【乾貨】最新阿里Android面試題總結(附答案)論讀Android源碼的重要性——Hook技術之View點擊劫持1、Android組件化項目在Android項目組件化之前,我們的項目都是像下圖那樣,一個單一工程下,根據不同的業務分幾個文件夾,把需要的第三方庫依賴下就開始開發了,這樣的代碼耦合嚴重,牽一髮而動全身,刪除某處代碼就會到處報錯,如果不解決掉報錯的地方,就沒法編譯打包,而且這樣的代碼只適合於個人開發,尤其團隊開發合併代碼的時候那真是一個麻煩,相信大家都會深有體會,如果項目很大的話,修改一點簡單的頁面都要重新編譯,Android編譯速度大家也都見識過,每次打包都很耗時,並且這樣的代碼想做單元測試也是無從下手
  • 「首席架構師看敏捷」核心實踐:測試驅動開發(TDD)簡介
    ATDD的目標是在準時(JIT)的基礎上為您的解決方案指定詳細的、可執行的需求。ATDD也被稱為行為驅動開發(BDD)。開發人員TDD。使用開發人員TDD,您可以編寫單個開發人員測試,有時不準確地稱為單元測試,然後編寫足夠的生產代碼來完成該測試。開發人員TDD的目標是在JIT的基礎上為您的解決方案指定一個詳細的、可執行的設計。