Jetpack

【Jetpack】Android Navigation 監聽 Fragment 與 Drawer Bottom Toolbar 完美配合 (四) 範例

【Jetpack】Android Navigation 監聽 Fragment 與 Drawer Bottom Toolbar 完美配合 (四) 範例

Android Jetpack Navigation 是一個統一的導航框架,可以讓開發者更容易地將 Android 皆常用的導航功能(如功能表、堆疊、抽屜和底部導航等)添加到應用中。

它為多個導航目的地提供一致的 API,並可以讓開發者將導航行為通用於單個或多個活動。


文章目錄

  1. Navigation Activity 監聽 Fragment
  2. Navigation Toolbar
  3. Navigation Navigation bar
  4. Navigation Navigation drawer
  5. Developer Documents Navigation

1.Navigation Activity 監聽 Fragment

AFragment.kt
class AFragment : Fragment(R.layout.fragment_a) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val binding = FragmentABinding.bind(view)

        binding.next.setOnClickListener {
            val action = AFragmentDirections.actionAFragmentToBFragment()
            findNavController().navigate(action)
        }
    }

}
BFragment.kt
class BFragment : Fragment(R.layout.fragment_b) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val binding = FragmentBBinding.bind(view)

        binding.next.setOnClickListener {
            val action = BFragmentDirections.actionBFragmentToCFragment2()
            findNavController().navigate(action)
        }
    }

}
CFragment.kt
class CFragment : Fragment(R.layout.fragment_c) {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val binding = FragmentCBinding.bind(view)
    }

}
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
    app:startDestination="@id/AFragment">

    <fragment
        android:id="@+id/AFragment"
        android:name="com.example.jetpackdemo.AFragment"
        android:label="AFragment"
        tools:layout="@layout/fragment_a">

        <action
            android:id="@+id/action_AFragment_to_BFragment"
            app:destination="@id/BFragment" />

    </fragment>

    <fragment
        android:id="@+id/BFragment"
        android:name="com.example.jetpackdemo.BFragment"
        android:label="BFragment"
        tools:layout="@layout/fragment_b">

        <action
            android:id="@+id/action_BFragment_to_CFragment2"
            app:destination="@id/CFragment2" />
    </fragment>

    <fragment
        android:id="@+id/CFragment2"
        android:name="com.example.jetpackdemo.CFragment"
        android:label="CFragment"
        tools:layout="@layout/fragment_c" />
</navigation>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?><!-- Use DrawerLayout as root container for activity -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/nav_graph" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    lateinit var actionBarDrawerToggle: ActionBarDrawerToggle

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()

        val navController = findNavController(R.id.nav_host_fragment)

        lifecycleScope.launch {
            navController.currentBackStackEntryFlow.collect {
                Log.e("now Fragment", it.destination.label.toString())
            }
        }
    }

}

2.Navigation Toolbar

activity_main.xml
<?xml version="1.0" encoding="utf-8"?><!-- Use DrawerLayout as root container for activity -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <androidx.appcompat.widget.LinearLayoutCompat
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <com.google.android.material.appbar.MaterialToolbar
            android:background="@color/material_dynamic_primary70"
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toTopOf="@+id/bottom_nav_view"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/nav_graph" />

    </androidx.appcompat.widget.LinearLayoutCompat>
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()

        val navController = findNavController(R.id.nav_host_fragment)
        NavigationUI.setupWithNavController(binding.toolbar, navController)

        navController.addOnDestinationChangedListener { _, destination, _ ->
            when (destination.id) {
                R.id.AFragment -> {
                    Log.e("now Fragment", "AFragment")
                }
                R.id.BFragment -> {
                    Log.e("now Fragment", "BFragment")
                }
                else -> {
                    Log.e("now Fragment", "CFragment")
                }
            }
        }
    }

}

3.Navigation Navigation bar

menu item id 必須跟 Navigation 的相同
bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/AFragment"
        android:icon="@drawable/dice"
        android:title="骰子" />

    <item
        android:id="@+id/BFragment"
        android:icon="@drawable/poker"
        android:title="撲克" />

    <item
        android:id="@+id/CFragment"
        android:icon="@drawable/game"
        android:title="遊戲" />
</menu>
nav_graph.xml
<?xml version="1.0" encoding="utf-8"?>
<navigation 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/nav_graph"
    app:startDestination="@id/AFragment">

    <fragment
        android:id="@+id/AFragment"
        android:name="com.example.jetpackdemo.AFragment"
        android:label="AFragment"
        tools:layout="@layout/fragment_a">
    </fragment>

    <fragment
        android:id="@+id/BFragment"
        android:name="com.example.jetpackdemo.BFragment"
        android:label="BFragment"
        tools:layout="@layout/fragment_b" />

    <fragment
        android:id="@+id/CFragment"
        android:name="com.example.jetpackdemo.CFragment"
        android:label="CFragment"
        tools:layout="@layout/fragment_c" />
</navigation>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">

    <data>

    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="0dp"
            android:layout_height="0dp"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toTopOf="@+id/bottom_nav_view"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/nav_graph" />

        <com.google.android.material.bottomnavigation.BottomNavigationView
            app:menu="@menu/bottom_nav_menu"
            app:itemRippleColor="@null"
            android:id="@+id/bottom_nav_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.bottomNavView.itemIconTintList = null

    }

    override fun onStart() {
        super.onStart()

        val navController = findNavController(R.id.nav_host_fragment)
        NavigationUI.setupWithNavController(binding.bottomNavView, navController)

        navController.addOnDestinationChangedListener { _, destination, _ ->
            when (destination.id) {
                R.id.AFragment -> {
                    Log.e("now Fragment", "AFragment")
                }
                R.id.BFragment -> {
                    Log.e("now Fragment", "BFragment")
                }
                else -> {
                    Log.e("now Fragment", "CFragment")
                }
            }
        }
    }

}

4.Navigation Navigation drawer

activity_main.xml
<?xml version="1.0" encoding="utf-8"?><!-- Use DrawerLayout as root container for activity -->
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

    </data>

    <androidx.drawerlayout.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="true">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_graph" />

        <com.google.android.material.navigation.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/bottom_nav_menu" />

    </androidx.drawerlayout.widget.DrawerLayout>
</layout>
MainActivity.kt
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    lateinit var actionBarDrawerToggle: ActionBarDrawerToggle

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    }

    override fun onStart() {
        super.onStart()

        supportActionBar?.setDisplayHomeAsUpEnabled(true)

        actionBarDrawerToggle = ActionBarDrawerToggle(this, binding.drawerLayout, R.string.app_name, R.string.app_name).apply {
            syncState()
            binding.drawerLayout.addDrawerListener(this)
        }

        binding.navView.itemIconTintList = null

        val navController = findNavController(R.id.nav_host_fragment)
        NavigationUI.setupWithNavController(binding.navView, navController)

        navController.addOnDestinationChangedListener { _, destination, _ ->
            when (destination.id) {
                R.id.AFragment -> {
                    Log.e("now Fragment", "AFragment")
                }
                R.id.BFragment -> {
                    Log.e("now Fragment", "BFragment")
                }
                else -> {
                    Log.e("now Fragment", "CFragment")
                }
            }
        }
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return if (actionBarDrawerToggle.onOptionsItemSelected(item)) true else super.onOptionsItemSelected(
            item
        )
    }

}

5.Developer Documents Navigation

Open in Documents Navigation

發表迴響