側邊欄是一個非常常用的一種功能,這次在 Android 嘗試通過做一個。
- 提供一個側邊欄
- 可以展開 / 收起 側邊欄
- 側邊欄中點選 item 可以切換主畫面的內容
Components
- Toolbar
- DrawerActivity
- Fragment
SideMenu
通過一個 DrawerActivity 做為容器,NavigationView 做為側邊欄,
當使用者點選不同的 item 時,通知 DrawerActivity 去切換當中的 fragment.
首先需要在 app/build.gradle 引入依賴包
1 2 3 |
dependencies { compile 'com.android.support:design:25.2.0' } |
Layout
建立側邊欄的內容
建立 /menu/drawer_view.xml 文件
通過 Group 可以實現類似 iOS 的 Section 的功能,可以為 Header 設定文字,並且會得到一條分割線。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
<?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/nav_1_fragment" android:icon="@drawable/icon_chicken" android:title="Chicken" /> <item android:title="Egg"> <menu> <group android:checkableBehavior="single"> <item android:id="@+id/nav_2_fragment" android:icon="@drawable/icon_egg1" android:title="Egg 1" /> <item android:id="@+id/nav_4_fragment" android:icon="@drawable/icon_egg3" android:title="Egg 2" /> <item android:id="@+id/nav_5_fragment" android:icon="@drawable/icon_egg4" android:title="Egg 3" /> <item android:id="@+id/nav_7_fragment" android:icon="@drawable/icon_egg6" android:title="Egg 4" /> </group> </menu> </item> <item android:title="Egg Group"> <menu> <group android:checkableBehavior="single"> <item android:id="@+id/nav_3_fragment" android:icon="@drawable/icon_egg2" android:title="Egg 5" /> <item android:id="@+id/nav_6_fragment" android:icon="@drawable/icon_egg5" android:title="Egg 6" /> </group> </menu> </item> </group> </menu> |
Header
我們建立 /res/layout/nav_header.xml 文件來為 NavigationView 提供一個 Header
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="192dp" android:background="?attr/colorPrimaryDark" android:padding="16dp" android:theme="@style/ThemeOverlay.AppCompat.Dark" android:orientation="vertical" android:gravity="bottom"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Nest" android:textColor="@android:color/white" android:textAppearance="@style/TextAppearance.AppCompat.Body1"/> </LinearLayout> |
通過在 activity_detail.xml 文件中的 <android.support.design.widget.NavigationView /> 加入屬性就可以引用了
1 |
app:headerLayout="@layout/nav_header" |
建立 Toolbar
為了能夠可以讓側邊欄可以在 ActionBar 上滑動,我們需要建立 Toolbar
/res/layout/toolbar.xml
1 2 3 4 5 6 7 8 9 10 11 |
<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/toolbar" android:layout_height="wrap_content" android:layout_width="match_parent" android:fitsSystemWindows="true" android:minHeight="?attr/actionBarSize" app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:background="?attr/colorPrimaryDark"> </android.support.v7.widget.Toolbar> |
需要特別注意的是這個屬性,這個值會影響 statusBar 是否會獨立佔有一定的空間。
1 |
android:fitsSystemWindows="true" |
DrawerActivity
我們建立了一個名為 DetailActiviy,並提供了幾個屬性,並初始化他們。
1 2 3 4 |
lateinit var drawer: DrawerLayout lateinit var toolbar: Toolbar lateinit var navigationView: NavigationView lateinit var fragmentManager: FragmentManager |
1 2 3 |
drawer = findViewById(R.id.drawer_layout) toolbar = findViewById(R.id.toolbar) navigationView = findViewById(R.id.nvView) |
設定漢堡按鈕
1 2 3 4 5 |
// set a toolbar to replace the actionBar val toggle = ActionBarDrawerToggle(this, drawer, toolbar, R.string.drawer_open, R.string.drawer_close) drawer.addDrawerListener(toggle) toggle.syncState() setSupportActionBar(toolbar) |
設定 DrawerActivity 初始化的 Fragment
1 2 3 |
// set default fragment fragmentManager = supportFragmentManager fragmentManager.beginTransaction().replace(R.id.flContent, Chicken1Fragment()).commit() |
建立一個 listener 用來監控使用者點擊側邊欄的狀況
1 2 |
// set navigation select Listener navigationView.setNavigationItemSelectedListener(navigationItemSelectedListener) |
發現使用者點擊時,我們將會做幾個動作
- 切換 DrawerActivity 中的 Fragement
- 修改 Title
- 設定 NavigationView 當前 checked 的 item 樣式
- 關閉 Drawer
和 iOS 不一樣的地方,在 drawer_view.xml 中,我們已經給每一個 item 設定一個 id,
所以我們也會通過這些 id 來判斷被 click 的 item 是哪個。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
private var navigationItemSelectedListener = NavigationView.OnNavigationItemSelectedListener { menuItem -> Log.e("selectDrawerItem","on ${menuItem.itemId}") when(menuItem.itemId) { R.id.nav_1_fragment -> { val fragment = Chicken1Fragment() fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit() } R.id.nav_2_fragment -> { val fragment = Chicken2Fragment() fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit() } R.id.nav_3_fragment -> { val fragment = Chicken3Fragment() fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit() } R.id.nav_4_fragment -> { val fragment = Chicken4Fragment() fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit() } R.id.nav_5_fragment -> { val fragment = Chicken5Fragment() fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit() } R.id.nav_6_fragment -> { val fragment = Chicken6Fragment() fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit() } R.id.nav_7_fragment -> { val fragment = Chicken7Fragment() fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit() } } menuItem.setChecked(true) setTitle(menuItem.title) drawer.closeDrawers() true } |
透明的 StatusBar
通過在 /res/values-v19/styles.xml 中的設定,可以讓 StatusBar 變透明
1 2 3 4 5 6 7 |
<resources> <!-- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <!-- Customize your theme here. --> <item name="android:windowTranslucentStatus">true</item> </style> </resources> |
Layout Editor
和 iOS 相比 Android 更方便的地方
在 Android 開發中,使用 Layout Editor 就像是 iOS 的 Storyboard 一樣,但有個更方便的地方。
如果我們建立了兩個 layout 文件(相當於 iOS 的兩個 xib 文件),這時候在其中一個 layout 中引入另外一個 layout ,
Android 的 Editor 是可以直接看到畫面呈現的效果的,而 iOS 需要在模擬器中才看得到。
初始化方法
在 iOS 中,我們通過下面的方法來將 XIB 中設定好的介面給初始化出來
1 |
public init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) |
而在 Android 中,可以通過下面的方法(runtime 中初始化)
1 2 |
val header = navigationView.inflateHeaderView(R.layout.nav_header) val headerPhotoImageView = header.findViewById(R.id.imageView) |
也可以直接在 Layout Editor 的 xml 文件中加入
1 |
app:headerLayout="@layout/nav_header" |
筆記
- TODO: 在側邊欄加入可展開多層的功能
- TODO: 更多 customise 的 layout
參考
- 官方文件 – Creating a Navigation Drawer
- 可以到 Github 上看對應的 Source Code