Android – Navigation Drawer 實作

Navigation Drawer 是 Google 讓使用 Android 裝置的人們,在使用不同 APP 時,能夠有個共通的操作模式,而制定的一個側邊欄元件。其實這也給所有的 Android 開發者有個統一的元件可用,省掉四處去找不同開源套件的麻煩;也因此才能寫這篇出來,啊不是…而官方定義的元件行為跟過去我們常看的樣子不相同,當然,這也是有這麼設計的理由在其中。

Navigation Drawer Opened

本篇將分成下面幾個部份為各位進行介紹

  1. 淺談 Navigation Drawer
  2. Navigation Drawer 的實作

1. 淺談 Navigation Drawer

Navigation Drawer 這個名詞對非 Android 的開發者來說,可能會稍嫌陌生,換個名詞-側邊選單 (Sliding menu),可能大家就比較有聽過了。而側邊選單這個功能,以目前來說最著名的 APP 就是 Facebook 了,點擊 APP 左上角的功能按鈕,整個畫面會往右邊划開,而畫面左邊會接著帶出幾近整個版面的選單畫面。

Sliding Menu(Facebook)

這個功能也忘了是從哪個在 iOS 上的 APP 開始使用後,再從 Facebook APP 普及到 Android 上來,這在某些層面上面也算是給某些被要求的 Android APP,要做出模仿位於 iOS APP 下方的 Tab Bar 的一種解法。而這個元件的開放原碼套件較為著名的也有兩個,一是 SlidingMenu,另一是 Simple-side-drawer,而這兩個套件通常也都搭配著 Action bar 來實作,所以要支援 Android 3.0 之前的裝置,就需要再配合 AcionBarSherlock 或是官方的 Support Library v7 的 appcomact 來使用。

而在大概是今年 (2013) 初,Google 官方定義了 Navigation Drawer 這個名詞,同時也將之列在屬於 Android APP 側邊欄這個動之專屬 Google 操作經驗之一,當然更可以在 Google 自家的 APP 中大幅地看到這個元件,像是 Google Play APP、Gmail APP、Youtube App、…都可以看到這樣的呈現。

Nevigation Drawer (Google Driver)

在官方定義的 Navigation Drawer,這個元件的動作已經不在是將整個 APP 的畫面往右邊推移,而是如上圖所示,是在 Action Bar 下方的區塊裡,從左邊划出的畫面,而這個畫面除了可以點選位在 Action Bar 上,該 APP Logo 左邊的三條橫線使之滑出外,亦可用手從螢幕的最左邊開始划向右邊拉出這個畫面。

這麼設計的原則,就是不希望讓使用者感到整個畫面是被搬動的,簡言之,這個元件該屬於在這個畫面中的其中一個元素,而且它有種被划出來的感覺,而不是被推出來的樣子;就像是我們點擊任何一個按鈕所跳出的小視窗,以該按鈕為中心順著某個方向滑出,對使用者的感覺,比起突然跳出來的視窗,多了那麼點溫柔的感覺(嗯…很微妙的形容 :P)。

那既然官方定義出這個名詞出來,他們也提供這個元件的函式庫 - Drawerlayout 給開發者們使用,他被放在 Support Library v7 裡面,也就是說,再搭配上 Action Bar – AppComat 套件(請見拙作的介紹),開發者們也可以很快地上手來使用之。

 

2. Navigation Drawer 的實作

那麼接下來就為各位說明,如何將 Navigation Drawer 實作在我們的 APP 裡,在開始前,大家先在 Android Developer 網頁裡取得我們要用的元素,整包 Action Bar Icon 下載到電腦裡後,(從下圖裡,相信大家也可以看到,被標示的按鈕之上,還有個官方範例下載,各位可以自行先抓下來看一看)

Get Action Bar Icon

解開的檔案目錄中有個名為「Navigation_Drawer_Indicator」的子目錄,裡面有兩個目錄,分別是讓我們可以用在黑底或是白底風格的 Action Bar 上,那就請各位依自己的需求選擇,把其中的四個目錄複製進我們專案的資源目錄中。

接下來,請各位先從 Mosil’s Github 中取得全部的程式碼,我們從 mosil_navigation_drawer_begin 開始一步一步地帶各位認得每個步驟。這個範例程式是從「Android – ActionBarCompat 基本套用」做了一點修改後而延續過來的起點,主要也是為了可以支援到 Android 2.2 (API Level 7) 其實是自己懶得再從頭開始,所以在取得專案後,請記得加入 android-support-v7-appcompat 到 library 裡面。

 

Check Point 1. 套入 DrawerLayout


這個階段從「mosil_navigation_drawer_begin」開始,請先如上前段所說明,把下載回來的 Navigation_Drawer_Indicator」目錄中的「holo_light」裡的四個子目錄複製到資源目錄「res」裡。再來看到 activity_main_xml 這個介面檔,我們要將最外面的「RelativeLayout」改為「android.support.v4.widget.DrawerLayout」並給予 id 設定。

整個 Layout 的最外圍定義好了後,其內容(Content)的元素至少要有一個是做 Drawer 用的;可是,一般狀況下,我們在這個畫面裡面,不會沒有內容,所以,這個範例中,就直接放入兩個 LinearLayout 元件,一個是放主要內容,另外一個就是負責 Drawer 的部份。

要留意做為 Drawer 的元件裡,layou_gravity 這個屬性,必需要被設定為 “start”或是 “left”,同時記得給予一個不會遮進整個畫面的固定寬度。到這邊基本的畫面就算是預備完成了。再來就看到 MainActivity.java 裡面,在程式裡面我們要先定義兩個物件:

  • DrawerLayout
    這個是整個 Navigation Drawer 的物件,也就是最外層被我們設定 id 為 @+id/drw_layout 的 DrawerLayout
  • ActionBarDrawerToggle
    當在 Action Bar 上的 Drawer Icon 被點擊時要做的動作

以下是 onCreate 的完整程式碼

接下來要在三個週期中去設定 Navigation Drawer 的動作,一個是在 onPostCreate 去同步 DrawerToggle 的狀態,這是確保 Activity 在被重啟生命週期後,Drawer 可以維持該有的長相,如其 icon;第二個是在 onConfigurationChanged,這主要比較會是在畫面有變化時要確保 Drawer 該有的動作;第三個在 Drawer 被點下時,也就是在 onOptionsItemSelected 該要做的動作。

MainActivity.java 的完整程式碼請見 Mosil’s Github,到這邊為止,基本的 Navigation Drawer 已經可以運作了,我們先來確定一下這邊該要做的幾個動作:

  • Layout 的設定:
    • 母體改為 Drawer Layout 元件
    • 內容放入兩個元件,分別做為主要內容跟 Drawer 所用的介面。
  • 主程式:
    • 宣告 Drawer 以及 DrawerToggle 兩個物件
    • 定義 Drawer 以及 DrawerToggle 該要做的事情
    • 打開 Up Button 圖示及其功能
    • 在 onPostCreate、onConfigurationChanged、onOptionsItemSelected,這三個週期裡進行各別該要執行的動作

完整的程式請見 Check Point 1

 

Check Point 2. 讓 Drawer 擁有側邊選單的能力


這個階段從「Check Point 1」往下進行。

看到這邊,可以知道,其實這個 DrawerLayout 元件,本身就是一個 Layout Widget,而實作出來的側邊欄-Drawer 其實也就是一個可以被放上其他介面元素的 Layout,所以,我們就從介面開始著手。

將原本在 activity_main.xml 中的 <!– Drawer Content –> 放入一個 ListView

  • drawer_menu_item.xml
    ListView Item 的 Layout,不用做得太複雜,這裡只用一個 TextView 做為試範,再請各位自行發揮嘍。

  • drawer_item.xml
    給清單物件使用的背景設定,存放在 res/drawable 中

再來就是主程式的部份,要另外宣告三個物件跟定義一個字串陣列

為了讓這個範例清楚,所以再來的動作全部集中到 setDrawerMenu() 這個函式裡面,對程式碼有特別要求的朋友,這邊就別計較了 😛

  • setDrawerMenu()
    就如同其字面意思,在這裡面要做的事情,就是把側邊欄裡面的項目全部設定好

  • selectMenuItem(int position)
    當清單物件被點擊後要執行的動作

做到這邊,可以說,大部份都已經完成了,再來就讓 title 的地方更新為我們選擇的項目,回到 onCreate 裡,定義 DrawerToggle 的動作中,有個 onDrawerClosed 的狀態,將之修改成下方這樣

完成後,執行起來就會如下圖所示,在 Navigation Drawer 中選擇其中一個項目後,其上方的 Title 就會被置換成該項目名稱,這時再點開 Navigation Drawer 來看時,該項目會被反白起來,而在 2.x 的設備上可能沒法像這樣被反白的呈現,可以留意一下。

Navigation Drawer CP2 Flow

在這個階段要注意到的部份如下

  • Layout
    • Drawer 的內容
    • ListView 的 Item Layout
  • 主程式
    • 宣告兩個 Layout 的 View,一個記錄現在在第幾個選項的 position,還有一個選項的內容陣列
    • 設定 Drawer 選單的內容
      • 定義兩個 View 物件
      • 將選項陣列設定到 ListView 中
    • 設定 ListView 的 Item 被點擊時要做的動作
    • 回頭修改當 Drawer 在 onClosed 的邏輯

完整的程式請見 Check Point 2

 

Final. 讓 Drawer 從右側滑出


這個階段會直接從 Check Point 2

讓 Drawer 從右側滑出的部份,其實很容易,只要在 activity_main.xml 裡將該介面元素加上去就可以了,他跟左邊的不同點就只在於 layout_gravity 這個屬性的設定而已,

在 layout_gravity 的屬性設定上只要注意一件事情,這裡能用的設定值只有 startleftendright,從字面上應該不難看出來,左側的 Drawer 可用的設定值就是 start 以及 left右側的即是 end 跟 right。至於要如何配對使用並沒有限制,但就是盡量維持一制的設定值吧。

這樣設定完畢後,兩邊的側邊欄都可以有動作了,頂多就是在主程式裡為右側 Drawer 帶入陰影,

雖然沒有任何按鈕去驅動右側的 Drawer,但以官方的對 Navigation Drawer 在操作上的定義來說,只要用手指從螢幕左或右的最邊邊,往另外一邊划動,就可以帶出 Drawer 介面。我們只要從右側最邊邊往左划動,就可以將右側邊欄帶出來。因為在操作上其實都跟左側邊欄的動作都差不多,所以接下來會著重在兩側邊欄介面的控制說明上了。筆者絕對不會說,只是因為懶得再去設定 menu 的 icon 而省略了這個步驟(踢飛)

如果大家一直跟著走到這邊,在操作的過程中,會發現一個很有趣的現象。那就是兩邊的 Drawer 有機會同時出現在畫面上!所以,接下來我們要做的一件事情就是讓兩側的介面在同一個時間裡,只有一邊會出現。在主程式的 onOptionsItemSelected 裡面進行程式碼的追加,

這邊邏輯很簡單,只要確定某一邊被開著,把就是把那邊關起來,而這邊就是直接拿右側來做判斷嘍!

可是!這時候又會變成一個狀況,大家可以自行先操作一下,觀察看看,倘若是要開啟其中一邊,另外一邊就像是在玩你追我跑的現象;可以先去看看 G+ 的 Android APP,用手從螢幕的兩邊各自往左右划動時,再怎樣都只有其中一邊會有動作而已。

接下來,要處理的地方是在 Drawer Toggle 裡面的 onDrawerOpened 以及 onDrawerClosed,這兩個 Drawer 的動作狀態

  • onDrawerOpened
    在其中一邊的 Drawer 被打開時,將另外一邊的 Drawer 鎖在 Closed 的狀態。

  • onDrawerClosed
    記得當某一邊被關上時,要將鎖定的狀態打開,不然沒地方去驅動開啟某一邊時,就永遠被鎖在關閉的狀態了 XD

DrawerLayout 的狀態設定值詳見官網說明

如此一來,兩側邊欄就不會出現你追我跑的狀況出現,那到這邊可能腦筋動比較快的人,會留意到一件事。當今天若是在一開始將兩邊都鎖起來,只要有地方去驅動、打開某一邊的介面就好啦!像是 G+ 點擊 Up Button,左側邊會出現;點通知鈴圖示,右介邊就會滑出,這樣就不用分別在 onDrawerOpened 以及 onDrawerClosed 做這麼麻煩的設定了!

的確,這樣可以減少一些麻煩,但若真的這麼做,有個動作就會無法被操作。那就是用手在螢幕的兩側往另外一側划動時,帶出該側的介面,而少了這個動作, Navigation Drawer 在官方定義的動裡,就少了那麼一味啦!

最後一個地方,只是一個小細節而已,在我們打開兩側邊欄時,標題(title)的文字都是 Open Drawer,我們其實也可以讓他如 G+ 那樣,在不同邊被划出時,呈現不同的文字。所以我們先去 string.xml 新增一個字串資源,

再來,回到 onDrawerOpened() 去做如下調整,

到這邊,整個 Navigation Drawer 的基礎操作,算是告個段落,最後小結一下這個階段要注意到的地方:

  • 在介面檔中增加右側邊的介面
  • 避開兩側有可能同時出現的狀況
  • 調整兩邊你追我跑的現象,並注意若是有上鎖某個側邊的狀態時,要記得解開
  • 針對不同邊的出現,在標題文字上的差異

此階段完整的程式碼,請見 Check Point Final

 

本部落格採用創用CC 姓名標示-非商業性-禁止改作 3.0 台灣 授權條款授權,如欲轉載請記得註明「莫希爾(Mosil) 手札

Loading Facebook Comments ...

9 關於 “Android – Navigation Drawer 實作” 的評論

  1. f2131772 hi 
    那個 R 檔是系統自動產生的,如果您是直接將程式碼複製貼上,請移除該行,引用自己專案中的 R 檔即可嘍~

  2. f2131772 是
    原則上您只要移除那行 import,就可以看到原有的 “R” 會被報錯,只要用自動更正,IDE 就會幫您自動添上正確的 import

  3. Mosil
    不好意思 我刪除import之後,然後Project  clean,還是出現紅色X
    原本的專案有引用android-support-v7-appcompat這個library,但也是出現紅色X,我就改成我的android-support-v7-appcompat,不過還是不行………請問你有email嗎?方便詢問你問題嗎?
    不好意思

  4. f2131772
    感覺上,您可能要先了解一下那個 R 檔的用途、怎麼使用 IDE 的快速修正先解掉問題。
    比方說,Android Studio 在有紅底抖線時,一是系統會跳提示,或是游標移到該處,鍵盤輸入「Alt + Enter」就會跳出修正提示。

  5. 請問  如果layout 是用 PreferenceScreen 有辦法包進去 android.support.v4.widget.DrawerLayout
    要怎麼包呢?

    試過發現好像沒辦法包

發表迴響

你的電子郵件位址並不會被公開。 必要欄位標記為 *