iT邦幫忙

2022 iThome 鐵人賽

DAY 20
1

Navigation drawer 預設情況下可以開啟或關閉,以適應不同的應用程式佈局,有兩種型別的Standard、Modal。Standard標準非常適合頻繁切換到不同的目的地。 Modal適合不頻繁但更集中的切換。

Navigation Drawers 有三種類型

(1). Modal navigation drawer

(2). Bottom navigation drawer

(3). Standard navigation drawer

https://ithelp.ithome.com.tw/upload/images/20221003/20144469dcVn3ZlWBr.png

M2 和 M3的差異

https://ithelp.ithome.com.tw/upload/images/20221003/20144469scpiHVVm63.png

M3 新增內容

  • 顏色:新的顏色對映和與動態顏色的相容性
  • 形狀:抽屜末端邊緣的圓角
  • 狀態:更新了顏色和形狀,以指示所選狀態

實際實作探討

API and source code:


1. Modal navigation drawer example

因為Modal navigation drawer 需要DrawerLayout
在Module層的build.Gradle的dependencies中新增AndroidX DrawerLayout library.—> implementation("androidx.drawerlayout:drawerlayout:1.1.1")
https://ithelp.ithome.com.tw/upload/images/20221004/20144469e0AHi4GsF5.png

In the layout

<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <com.google.android.material.appbar.AppBarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <!-- Center-aligned、Small-->
            <com.google.android.material.appbar.MaterialToolbar
                android:id="@+id/topAppBar"
                android:layout_width="match_parent"
                android:layout_height="?attr/actionBarSize"
                app:layout_scrollFlags="scroll|enterAlways|snap"
                app:menu="@menu/top_app_bar"
                app:navigationIcon="@drawable/ic_menu_24"
                app:title="page_title" />

        </com.google.android.material.appbar.AppBarLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.navigation.NavigationView
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:headerLayout="@layout/header_navigation_drawer"
        app:menu="@menu/navigation_drawer"
        android:layout_gravity="start" />

</androidx.drawerlayout.widget.DrawerLayout>

res/values/themes.xml

<style name="Theme.App" parent="Theme.Material3.Light.NoActionBar">
    <item name="android:windowTranslucentStatus">true</item>
</style>

res/layout/header_navigation_drawer.xml

  • android:fitsSystemWindows="true" 用於根據狀態欄等系統窗口調整視圖佈局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:layout_marginStart="24dp"
        android:layout_marginEnd="24dp"
        android:textAppearance="?attr/textAppearanceHeadlineSmall"
        android:textColor="?attr/colorOnSurface"
        android:text="Header_title" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="24dp"
        android:layout_marginStart="24dp"
        android:layout_marginEnd="24dp"
        android:textAppearance="?attr/textAppearanceTitleSmall"
        android:textColor="?attr/colorOnSurfaceVariant"
        android:text="Header_text" />

</LinearLayout>

In code

  • drawerLayout.open()
  • drawerLayout.close()
class Navigation : AppCompatActivity() {

    private lateinit var binding: NavigationDrawerModelBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = NavigationDrawerModelBinding.inflate(layoutInflater)
        setContentView(binding.root)

        binding.topAppBar.setNavigationOnClickListener {
            binding.drawerLayout.open()
        }

        binding.navigationView.setNavigationItemSelectedListener { menuItem ->
            menuItem.isChecked = true
            binding.drawerLayout.close()
            true
        }
    }
}

2. Bottom navigation drawer example

https://ithelp.ithome.com.tw/upload/images/20221004/20144469WpmWTcfR0b.png

In the layout

<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="?attr/actionBarSize">

        <!-- Screen content -->

        <FrameLayout
            android:id="@+id/scrim"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <com.google.android.material.navigation.NavigationView
            android:id="@+id/navigationView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:behavior_hideable="true"
            app:headerLayout="@layout/header_navigation_drawer"
            app:layout_behavior="@string/bottom_sheet_behavior"
            app:menu="@menu/navigation_drawer" />

    </androidx.coordinatorlayout.widget.CoordinatorLayout>

    <com.google.android.material.bottomappbar.BottomAppBar
        android:id="@+id/bottomAppBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        app:navigationIcon="@drawable/ic_menu_24" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

In code

class Navigation : AppCompatActivity() {

    private lateinit var binding: NavigationDrawerBottomBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = NavigationDrawerBottomBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val bottomSheetBehavior = BottomSheetBehavior.from(binding.navigationView)
        bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN

        // bottomAppBar點擊icon開啟navigationView
        binding.bottomAppBar.setNavigationOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
        }

        // navigationView的ItemSelected點擊事件
        binding.navigationView.setNavigationItemSelectedListener { menuItem ->
            menuItem.isChecked = true
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
            true
        }

        // navigationView以外的地方點擊關閉
        binding.scrim.setOnClickListener {
            bottomSheetBehavior.state = BottomSheetBehavior.STATE_HIDDEN
        }

        // Bottom navigation drawer 上滑時,navigationView以外的地方背景設改變
        bottomSheetBehavior.addBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
            override fun onSlide(bottomSheet: View, slideOffset: Float) {
                val baseColor = Color.BLACK
                //60% 不透明度
                val baseAlpha = ResourcesCompat.getFloat(resources, com.google.android.material.R.dimen.material_emphasis_medium)
                // Map slideOffset from [-1.0, 1.0] to [0.0, 1.0]
                val offset = (slideOffset - (-1f)) / (1f - (-1f)) * (1f - 0f) + 0f
                val alpha = MathUtils.lerp(0f, 255f, offset * baseAlpha).toInt()
                val color = Color.argb(alpha, baseColor.red, baseColor.green, baseColor.blue)
                binding.scrim.setBackgroundColor(color)
            }
            override fun onStateChanged(bottomSheet: View, newState: Int) {
            }
        })

    }
}

3. Standard navigation drawer example

放置畫面上與螢幕內容互動,可用於平板電腦和桌上型電腦,因尺寸原因不適合手機裝置。

https://ithelp.ithome.com.tw/upload/images/20221003/2014446923UtZBYAbX.png

layout 中新增 NavigationView,設定headerLayoutmenu的屬性

  • app:headerLayout="@layout/header_navigation_drawer"
  • app:menu="@menu/navigation_drawer”
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    
    <com.google.android.material.navigation.NavigationView
				android:id="@+id/navigationView"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
				app:headerLayout="@layout/header_navigation_drawer"
				app:menu="@menu/navigation_drawer”
        app:layout_constraintStart_toStartOf="parent"/>

		<TextView
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="40dp"
            android:text="content pane here"
            android:textColor="?attr/colorOnSurface"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/navigationView"
            app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

主題—> res/values/themes.xml

  • <item name="android:windowTranslucentStatus">true</item> 設定透明status bar ****
<style name="Theme.App" parent="Theme.Material3.Light.NoActionBar">
    <item name="android:windowTranslucentStatus">true</item>
</style>

新增 headerLayout 標題和副標題 —> res/layout/header_navigation_drawer.xml

  • android:fitsSystemWindows="true" 用於根據狀態欄等系統窗口調整視圖佈局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true"
    android:orientation="vertical">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:layout_marginStart="24dp"
        android:layout_marginEnd="24dp"
        android:textAppearance="?attr/textAppearanceHeadlineSmall"
        android:textColor="?attr/colorOnSurface"
        android:text="Header_title" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="24dp"
        android:layout_marginStart="24dp"
        android:layout_marginEnd="24dp"
        android:textAppearance="?attr/textAppearanceTitleSmall"
        android:textColor="?attr/colorOnSurfaceVariant"
        android:text="Header_text" />

</LinearLayout>

新增 menu item

<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@+id/main_item"
        android:title="Mail">

        <menu>
            <item
                android:id="@+id/inbox_item"
                android:icon="@drawable/ic_inbox"
                android:title="inbox"
                android:checkable="true"/>
            <item
                android:id="@+id/outbox_item"
                android:icon="@drawable/ic_send"
                android:title="outbox"
                android:checkable="true"/>
            <item
                android:id="@+id/favourites_item"
                android:icon="@drawable/ic_favorite_border_24"
                android:title="favourites"
                android:checkable="true"/>
        </menu>
        
    </item>

</menu> 

完成後在手機、折疊手機、平板的呈現樣子
https://ithelp.ithome.com.tw/upload/images/20221003/20144469NmEfIy5yr5.png


Anatomy

https://ithelp.ithome.com.tw/upload/images/20221004/20144469P6FTSBUqYS.png

attributes相關設定

Modal navigation drawer Layout

https://ithelp.ithome.com.tw/upload/images/20221004/20144469syU5gLSKv9.png

Standard navigation drawer Layout

https://ithelp.ithome.com.tw/upload/images/20221004/20144469AbKxy2cpE9.png

感謝您看到這邊 /images/emoticon/emoticon29.gif

參考資料:Material Deaign Navigation drawer


上一篇
Day19 延續前實戰新增Bottom Sheets、Full-screen dialogs實作
下一篇
Day21 使用M3的Navigation rail
系列文
Kotlin 實踐 Material Design 懶人包30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言