遊玩 Navigation Drawer 大集合
前言
導航欄是現在 APP 很常見的功能元件,在 Android UI design guide 中,說到它是用來前往該 APP 其他功能頁或使用上的終結點(像是帳號轉換),在畫面上既可以常駐 ,也可以收在導航按鈕中。既然是現在的主流設計,官方也推出了封裝的類別,能更快速的建構這樣使用習慣的 APP,也能統一該 UI 的風格,於是這次便要來透徹了解在 Android SDK version 22.2.0 加入的 NavigationView。
概觀
先從官方的教學文件開始吧!看完文件我們可以在腦中得到下面這個大概的架構圖:
現在我們知道大致上要怎麼用這個組合,至少有一點概念了,接著要透過了解更多原生實作細節,讓我們更充分的明白自己手上握有的工具,進一步如何客製化。所以現在我們就逐個突破,來看看內部到底是封裝了什麼東西吧。
/* 在創建新專案時可以透過選擇 Navigation Drawer Activity 快速獲得一個寫好的模板 */
DrawerLayout
我們先看看簡單的 DrawerLayout 架構:
(下圖只列出我覺得比較需要關注的部分)
(下圖只列出我覺得比較需要關注的部分)
看 DrawerLayout 的源碼,可以知道它已經實作了拖拉動畫和互動的部分,也就是 ViewDragHelper 的部分,再利用實作 ViewDragCallBack 可以作為跨接溝通,最後封裝完公開出兩個控制函式: openDrawer 、 closeDrawer ,使外部使用變得只需要簡單的指定開/關,再透過傳遞的參數 EdgeGravity 就能找到要做互動的畫面,做完互動了。
EdgeGravity 也就是在 xml 檔的設定,使用上的配對就是這樣:
openDrawer(Gravity.START) <-><-> View.getLayoutGravity == ->->Gravity.START
這次的主畫面 xml 檔 : activity_main.xml
<android.support.v4.widget.DrawerLayout 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:id="@+id/drawer_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" tools:openDrawer="start"> <include layout="@layout/app_bar_main" android:layout_width="match_parent" android:layout_height="match_parent"/> <android.support.design.widget.NavigationView android:id="@+id/nav_view" android:layout_width="wrap_content" android:layout_height="match_parent" android:layout_gravity="start" android:fitsSystemWindows="true" app:menu="@menu/activity_main_drawer"/> </android.support.v4.widget.DrawerLayout>
-> 看看各種拖拉嘗試
接著我們將重點放在前面也有提到的 DrawerListener,可以看到在 MainActivity onCreate 內直接創建了一個名叫 ActionBarDrawerToggle 的類別,該類別便是實作了介面 DrawerListener,所以直接將它傳給 DrawerLayout,這邊就設定完成了。
如何使用封裝實作後的 ActionBarDrawerToggle :
val toggle = ActionBarDrawerToggle( this, drawer_layout, toolbar,
R.string.navigation_drawer_open, R.string.navigation_drawer_close) drawer_layout.addDrawerListener(toggle) toggle.syncState()
ActionBarDrawerToggle 內部也實作了函式 onOptionsItemSelected,並會由activity 的 onOptionsItemSelected 來驅動,藉此接收到來自 Toolbar 導航按鈕的點擊事件。
onOptionsItemSelected 的實作 :
public boolean onOptionsItemSelected(MenuItem item) {
if (item != null && item.getItemId() == android.R.id.home && mDrawerIndicatorEnabled) { toggle(); // 封裝了選擇 closeDrawer 或 openDrawer return true; } return false; }
還有要注意的就是在 MainActivity 要記得處理返回鍵,點擊時先關閉導航欄。
override fun onBackPressed() { if (drawer_layout.isDrawerOpen(GravityCompat.START)) { drawer_layout.closeDrawer(GravityCompat.START) } else { super.onBackPressed() } }
NavigationView
一樣我們先看看簡單的 NavigationView 架構:
這邊可以看到 NavigationView 的父類別有 FrameLayout 可以猜到其實這個 View 就是官方刻好的畫面,由 Menu 物件來組成,NavigationMenu 便是繼承實作 Menu 定義的介面的類別,Menu 介面主要描述了選單物件,需要能被增加、刪減,然後能群組選項與基本的查找選項畫面。
繪製畫面的部分,需要透過 MenuInflater 在創建時 inflate MenuItem,再綁回 NavigationMenu。關於怎們操作物件互動我們可以先看從 NavigationMenu 繼承的 MenuBuilder 找端倪,就發現了 Presenter 的蹤影了。'
先是用 WeakReference 的方式指向被傳進來的 Presenter,保留物件仍能被自動回收的參照,接著請 Presenter 執行選單初始化,所以我們現在可以正式來看 NavigationMenuPresenter 了。
這邊還是稍微畫下類別關係圖好了:
NavigationMenuView 本身繼承了 RecyclerView,所以這其實就是一個有 Header 的 Recycler 畫面,並在 NavigationMenuAdapter 裡轉接了 ArrayList,而它的佈局設置也已經寫好了,這也使得客製化程度降低。這邊基本上只能透過程式動態修改了。
而這邊用到的 CallBack 是在 MenuPresenter 定義的介面,能夠監聽 onCloseMenu / onOpenSubMenu。
最後就是公開的 OnNavigationItemSelectedListener 介面,它能去監聽擁有的 MenuItem 點擊事件,在範例裡,是直接由 MainActivity 實作了。
繪製畫面的部分,需要透過 MenuInflater 在創建時 inflate MenuItem,再綁回 NavigationMenu。關於怎們操作物件互動我們可以先看從 NavigationMenu 繼承的 MenuBuilder 找端倪,就發現了 Presenter 的蹤影了。'
加入Presenter的部分與真的初始化 Menu :
public void addMenuPresenter(MenuPresenter presenter, Context menuContext) { mPresenters.add(new WeakReference<MenuPresenter>(presenter)); presenter.initForMenu(menuContext, this); mIsActionItemsStale = true; }
先是用 WeakReference 的方式指向被傳進來的 Presenter,保留物件仍能被自動回收的參照,接著請 Presenter 執行選單初始化,所以我們現在可以正式來看 NavigationMenuPresenter 了。
這邊還是稍微畫下類別關係圖好了:
NavigationMenuView 本身繼承了 RecyclerView,所以這其實就是一個有 Header 的 Recycler 畫面,並在 NavigationMenuAdapter 裡轉接了 ArrayList
<android.support.design.internal.NavigationMenuItemView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="?attr/listPreferredItemHeightSmall" android:paddingLeft="?attr/listPreferredItemPaddingLeft" android:paddingRight="?attr/listPreferredItemPaddingRight" android:foreground="?attr/selectableItemBackground" android:focusable="true"/>
而這邊用到的 CallBack 是在 MenuPresenter 定義的介面,能夠監聽 onCloseMenu / onOpenSubMenu。
最後就是公開的 OnNavigationItemSelectedListener 介面,它能去監聽擁有的 MenuItem 點擊事件,在範例裡,是直接由 MainActivity 實作了。
更多:自己刻 NavigationView 可以多 Free?
整合
合起來後,我們可以看到整個簡單的架構如下圖,元件彼此間的溝通跟關係就非常明瞭了!
留言
張貼留言