Loading... ## 前言 Google官方在19年的I/O大会上推出JetPack组件,这个组件应该算是为目前的Android开发领域锦上添花,不过出于好奇,还是一探究竟吧。 ## Jetpack Navigation是什么 关于Jetpack Navigation的介绍可以参见<span class="external-link"><a href="https://www.youtube.com/watch?v=JFGq0asqSuA" target="_blank">视频[Jetpack Navigation (Google I/O'19)]<i data-feather='external-link'></i></a></span>(PS:观前请先确认能够科学上网) **官方介绍摘抄:** Navigation是指支持用户Navigation、进入和退出应用中不同内容片段的交互。Android Jetpack 的Navigation组件可帮助您实现Navigation,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。Navigation组件还通过遵循<span class="external-link"><a href="https://developer.android.google.cn/guide/navigation/navigation-principles" target="_blank">一套既定原则<i data-feather='external-link'></i></a></span>来确保一致且可预测的用户体验。 ## 怎么使用Jetpack Navigation ### 官方开发文档 <span class="external-link"><a href="https://developer.android.google.cn/guide/navigation" target="_blank">文档链接<i data-feather='external-link'></i></a></span> (PS:观前请先确认能够科学上网) ### 官方Demo <span class="external-link"><a href="https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample" target="_blank">NavigationAdvancedSample<i data-feather='external-link'></i></a></span> ### 我的实践记录 #### 开发需求: 实现一个包含记账、统计、高级功能和设置的app。 #### UI结构: <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAAA1JREFUCJljOHz4cAMAB2ACypfyMOEAAAAASUVORK5CYII=" data-original="https://s1.ax1x.com/2020/08/02/aYteaQ.png" alt="UI" /> #### 代码实现步骤: **实现方式1(1个Activity+1个导航+N个Fragment):** 1. 在res->navigation文件夹下创建nav_graph文件,文件内包含UI结构中的4个Fragment,分别是BillFragment、StatisticsFragment、AdvancedFragment、MineFragment,该4个Fragment都是全局Fragment。nav_graph.xml内的布局如下: ```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/billFragment"> <fragment android:id="@+id/billFragment" android:name="com.litchiny.jetpackdemo.BillFragment" android:label="fragment_bill" tools:layout="@layout/fragment_bill" > </fragment> <fragment android:id="@+id/statisticsFragment" android:name="com.litchiny.jetpackdemo.StatisticsFragment" android:label="fragment_statistics" tools:layout="@layout/fragment_statistics" /> <fragment android:id="@+id/advancedFragment" android:name="com.litchiny.jetpackdemo.AdvancedFragment" android:label="fragment_advanced" tools:layout="@layout/fragment_advanced" /> <fragment android:id="@+id/mineFragment" android:name="com.litchiny.jetpackdemo.MineFragment" android:label="fragment_mine" tools:layout="@layout/fragment_mine" /> <action android:id="@+id/action_global_billFragment" app:destination="@id/billFragment"/> <action android:id="@+id/action_global_statisticsFragment" app:destination="@id/statisticsFragment" /> <action android:id="@+id/action_global_advancedFragment" app:destination="@id/advancedFragment"/> <action android:id="@+id/action_global_mineFragment" app:destination="@id/mineFragment"/> </navigation> ``` 说明:其中设置各个Fragment的id、name、lable、layout。 2. 在Mainactivity的activity_main中设置navigation,目的是将Activity与Fragment进行绑定处理。其中UI Bottom位置的切换按钮使用BottomNavigationView+Menu实现,Menu中的itemId需与Navigation中的fragment的id保持一致,否则会出现绑定失败。 activity_main.xml内的布局如下: ```xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <fragment android:id="@+id/nav_fragment" android:name="androidx.navigation.fragment.NavHostFragment" android:layout_width="0dp" android:layout_height="0dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" app:defaultNavHost= "true" app:navGraph="@navigation/nav_graph" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:layout_width="match_parent" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" android:id="@+id/bottom_nav" app:menu="@menu/menu_bottom_nav" /> </androidx.constraintlayout.widget.ConstraintLayout> ``` menu_bottom_nav.xml的布局如下: ``` <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <item android:id="@+id/billFragment" android:icon="@drawable/bottom_icon_bill" android:orderInCategory="1" android:title="Bill" app:showAsAction="always" /> <item android:id="@+id/statisticsFragment" android:icon="@drawable/bottom_icon_satistics" android:orderInCategory="2" android:title="Satis" app:showAsAction="always" /> <item android:id="@+id/advancedFragment" android:icon="@drawable/bottom_icon_advanced" android:orderInCategory="2" android:title="Advanced" app:showAsAction="always" /> <item android:id="@+id/mineFragment" android:icon="@drawable/bottom_icon_mine" android:orderInCategory="2" android:title="Mine" app:showAsAction="always" /> </menu> ``` 说明:item跟各Fragment直接绑定。 3. 在MainActivity中将BottomNavigationView与NavController进行绑定。代码如下: ``` class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navController = findNavController(R.id.nav_fragment) findViewById<BottomNavigationView>(R.id.bottom_nav) .setupWithNavController(navController) } } ``` 4. 最终实现效果如下(PS:所有的icon来自<span class="external-link"><a href="https://www.iconfont.cn/" target="_blank">阿里巴巴矢量库<i data-feather='external-link'></i></a></span>): <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAAA1JREFUCJljOHz4cAMAB2ACypfyMOEAAAAASUVORK5CYII=" data-original="https://s1.ax1x.com/2020/08/03/aNbG01.gif" alt="效果" /> **实现方式2(1个Activity+N个导航+N个Fragment):** 代码参考<span class="external-link"><a href="https://github.com/android/architecture-components-samples/tree/master/NavigationAdvancedSample" target="_blank">NavigationAdvancedSample<i data-feather='external-link'></i></a></span> 1. 4个Fragment对应4个Navigation,分别在res->navigation目录下创建nav_bill、nav_statistics、nav_advanced、nav_mine 4个布局文件,其中各布局文件中包含Fragment+FragmentDetail。 nav_bill.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_bill" app:startDestination="@id/billFragment2"> <fragment android:id="@+id/billFragment2" android:name="com.litchiny.jetpackdemo.ui.fragment.bill.BillFragment2" android:label="fragment_bill2" tools:layout="@layout/fragment_bill2" > <action android:id="@+id/action_billFragment2_to_billFragment2Detail" app:destination="@id/billFragment2Detail" /> </fragment> <fragment android:id="@+id/billFragment2Detail" android:name="com.litchiny.jetpackdemo.ui.fragment.bill.BillFragment2Detail" android:label="fragment_bill_fragment2_detail" tools:layout="@layout/fragment_bill_fragment2_detail" /> </navigation> ``` nav_statistics.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_statictics" app:startDestination="@id/statisticsFragment2"> <fragment android:id="@+id/statisticsFragment2" android:name="com.litchiny.jetpackdemo.ui.fragment.statistics.StatisticsFragment2" android:label="fragment_statistics2" tools:layout="@layout/fragment_statistics2" > <action android:id="@+id/action_statisticsFragment2_to_statisticsFragment2Detail" app:destination="@id/statisticsFragment2Detail" /> </fragment> <fragment android:id="@+id/statisticsFragment2Detail" android:name="com.litchiny.jetpackdemo.ui.fragment.statistics.StatisticsFragment2Detail" android:label="fragment_statistics_fragment2_detail" tools:layout="@layout/fragment_statistics_fragment2_detail" /> </navigation> ``` nav_advanced.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_advanced" app:startDestination="@id/advancedFragment2"> <fragment android:id="@+id/advancedFragment2" android:name="com.litchiny.jetpackdemo.ui.fragment.advanced.AdvancedFragment2" android:label="fragment_advanced2" tools:layout="@layout/fragment_advanced2" > <action android:id="@+id/action_advancedFragment2_to_advancedFragment2Detail" app:destination="@id/advancedFragment2Detail" /> </fragment> <fragment android:id="@+id/advancedFragment2Detail" android:name="com.litchiny.jetpackdemo.ui.fragment.advanced.AdvancedFragment2Detail" android:label="fragment_advanced_fragment2_detail" tools:layout="@layout/fragment_advanced_fragment2_detail" /> </navigation> ``` nav_mine.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_mine" app:startDestination="@id/mineFragment2"> <fragment android:id="@+id/mineFragment2" android:name="com.litchiny.jetpackdemo.ui.fragment.mine.MineFragment2" android:label="fragment_mine2" tools:layout="@layout/fragment_mine2" > <action android:id="@+id/action_mineFragment2_to_mineFragment2Detail" app:destination="@id/mineFragment2Detail" /> </fragment> <fragment android:id="@+id/mineFragment2Detail" android:name="com.litchiny.jetpackdemo.ui.fragment.mine.MineFragment2Detail" android:label="fragment_mine_fragment2_detail" tools:layout="@layout/fragment_mine_fragment2_detail" /> </navigation> ``` 2. 创建 MainActivity2.kt,同时修改activity_main2的布局内容。 activity_main2.xml布局如下: ``` <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent" android:layout_height="match_parent" tools:context=".ui.MainActivity2"> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_container" android:layout_width="match_parent" android:layout_height="match_parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <com.google.android.material.bottomnavigation.BottomNavigationView android:id="@+id/bottom_nav" 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" app:menu="@menu/menu_bottom_nav2" /> </androidx.constraintlayout.widget.ConstraintLayout> ``` 在res->menu目录下创建menu_bottom_nav2,menu_bottom_nav2.xml布局如下: ``` <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/nav_bill" android:icon="@drawable/bottom_icon_bill" android:contentDescription="Bill" android:title="Bill" /> <item android:id="@+id/nav_statictics" android:icon="@drawable/bottom_icon_satistics" android:contentDescription="Satis" android:title="Satis" /> <item android:id="@+id/nav_advanced" android:icon="@drawable/bottom_icon_advanced" android:contentDescription="Advan" android:title="Advan" /> <item android:id="@+id/nav_mine" android:icon="@drawable/bottom_icon_mine" android:contentDescription="Mine" android:title="Mine" /> </menu> ``` 说明:值得注意的是,Menu的item id绑定的各导航id,因为每次切换Bottom Icon的时候,直接切换的是各导航内的Fragment。 3. 在MainActivity2中实现了以下内容: * BottomNavigationView与Navigation的UI绑定 和切换UI * Actionbar的更新Title、back点击返回上一级。 MainActivity2.kt的代码如下: ``` class MainActivity2 : AppCompatActivity() { private var currentNavController: LiveData<NavController>? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main2) if (savedInstanceState == null) setupBottomNavigationBar() } override fun onRestoreInstanceState(savedInstanceState: Bundle) { super.onRestoreInstanceState(savedInstanceState) setupBottomNavigationBar() } private fun setupBottomNavigationBar() { val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav) val navGraphIds = listOf(R.navigation.nav_bill,R.navigation.nav_statictics,R.navigation.nav_advanced,R.navigation.nav_mine) val controller = bottomNavigationView.setupWithNavController( navGraphIds = navGraphIds, fragmentManager = supportFragmentManager, containerId = R.id.nav_host_container, intent = intent ) controller.observe(this, Observer { navController -> setupActionBarWithNavController(navController) }) currentNavController = controller } override fun onSupportNavigateUp(): Boolean { return currentNavController?.value?.navigateUp() ?: super.onSupportNavigateUp() } } ``` 因为是多导航的关系,所以创建了NavigationExtensions.kt文件,其中重写了BottomNavigationView.setupWithNavController方法,方法内实现了以下内容: * 获取FragmentTag List, * 设置初始Fragment * 设置setOnNavigationItemSelectedListener监听,并在监听里根据Menu的ItemId获取实时NavHostFragment * 由fragmentManager切换UI。 NavigationExtensions.kt代码如下: ``` /* * Copyright 2019, The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.litchiny.jetpackdemo import android.content.Intent import android.util.Log import android.util.SparseArray import androidx.core.util.forEach import androidx.core.util.set import androidx.fragment.app.FragmentManager import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.navigation.NavController import androidx.navigation.fragment.NavHostFragment import com.google.android.material.bottomnavigation.BottomNavigationView /** * Manages the various graphs needed for a [BottomNavigationView]. * * This sample is a workaround until the Navigation Component supports multiple back stacks. */ fun BottomNavigationView.setupWithNavController( navGraphIds: List<Int>, fragmentManager: FragmentManager, containerId: Int, intent: Intent ): LiveData<NavController> { // Map of tags val graphIdToTagMap = SparseArray<String>() // Result. Mutable live data with the selected controlled val selectedNavController = MutableLiveData<NavController>() var firstFragmentGraphId = 0 // First create a NavHostFragment for each NavGraph ID navGraphIds.forEachIndexed { index, navGraphId -> val fragmentTag = getFragmentTag(index) // Find or create the Navigation host fragment val navHostFragment = obtainNavHostFragment( fragmentManager, fragmentTag, navGraphId, containerId ) // Obtain its id val graphId = navHostFragment.navController.graph.id if (index == 0) { firstFragmentGraphId = graphId } // Save to the map graphIdToTagMap[graphId] = fragmentTag // Attach or detach nav host fragment depending on whether it's the selected item. if (this.selectedItemId == graphId) { // Update livedata with the selected graph selectedNavController.value = navHostFragment.navController attachNavHostFragment(fragmentManager, navHostFragment, index == 0) } else { detachNavHostFragment(fragmentManager, navHostFragment) } Log.d( "Litchiny", "53---fragmentTag: $fragmentTag,graphId: $graphId,selectedItemId: $selectedItemId" ) } // Now connect selecting an item with swapping Fragments var selectedItemTag = graphIdToTagMap[this.selectedItemId] val firstFragmentTag = graphIdToTagMap[firstFragmentGraphId] var isOnFirstFragment = selectedItemTag == firstFragmentTag // When a navigation item is selected setOnNavigationItemSelectedListener { item -> // Don't do anything if the state is state has already been saved. if (fragmentManager.isStateSaved) { false } else { val newlySelectedItemTag = graphIdToTagMap[item.itemId] Log.d("Litchiny", "item.itemId:${item.itemId}") if (selectedItemTag != newlySelectedItemTag) { // Pop everything above the first fragment (the "fixed start destination") fragmentManager.popBackStack( firstFragmentTag, FragmentManager.POP_BACK_STACK_INCLUSIVE ) val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment // Exclude the first fragment tag because it's always in the back stack. if (firstFragmentTag != newlySelectedItemTag) { // Commit a transaction that cleans the back stack and adds the first fragment // to it, creating the fixed started destination. fragmentManager.beginTransaction() .setCustomAnimations( R.anim.nav_default_enter_anim, R.anim.nav_default_exit_anim, R.anim.nav_default_pop_enter_anim, R.anim.nav_default_pop_exit_anim ) .attach(selectedFragment) .setPrimaryNavigationFragment(selectedFragment) .apply { // Detach all other Fragments graphIdToTagMap.forEach { _, fragmentTagIter -> if (fragmentTagIter != newlySelectedItemTag) { detach(fragmentManager.findFragmentByTag(firstFragmentTag)!!) } } } .addToBackStack(firstFragmentTag) .setReorderingAllowed(true) .commit() } selectedItemTag = newlySelectedItemTag isOnFirstFragment = selectedItemTag == firstFragmentTag selectedNavController.value = selectedFragment.navController true } else { false } } } // Optional: on item reselected, pop back stack to the destination of the graph setupItemReselected(graphIdToTagMap, fragmentManager) // Handle deep link // setupDeepLinks(navGraphIds, fragmentManager, containerId, intent) // Finally, ensure that we update our BottomNavigationView when the back stack changes fragmentManager.addOnBackStackChangedListener { if (!isOnFirstFragment && !fragmentManager.isOnBackStack(firstFragmentTag)) { this.selectedItemId = firstFragmentGraphId } // Reset the graph if the currentDestination is not valid (happens when the back // stack is popped after using the back button). selectedNavController.value?.let { controller -> if (controller.currentDestination == null) { controller.navigate(controller.graph.id) } } } return selectedNavController } private fun BottomNavigationView.setupDeepLinks( navGraphIds: List<Int>, fragmentManager: FragmentManager, containerId: Int, intent: Intent ) { navGraphIds.forEachIndexed { index, navGraphId -> val fragmentTag = getFragmentTag(index) // Find or create the Navigation host fragment val navHostFragment = obtainNavHostFragment( fragmentManager, fragmentTag, navGraphId, containerId ) // Handle Intent if (navHostFragment.navController.handleDeepLink(intent) && selectedItemId != navHostFragment.navController.graph.id ) { this.selectedItemId = navHostFragment.navController.graph.id } } } private fun BottomNavigationView.setupItemReselected( graphIdToTagMap: SparseArray<String>, fragmentManager: FragmentManager ) { setOnNavigationItemReselectedListener { item -> val newlySelectedItemTag = graphIdToTagMap[item.itemId] val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment val navController = selectedFragment.navController // Pop the back stack to the start destination of the current navController graph navController.popBackStack( navController.graph.startDestination, false ) } } private fun detachNavHostFragment( fragmentManager: FragmentManager, navHostFragment: NavHostFragment ) { fragmentManager.beginTransaction() .detach(navHostFragment) .commitNow() } private fun attachNavHostFragment( fragmentManager: FragmentManager, navHostFragment: NavHostFragment, isPrimaryNavFragment: Boolean ) { fragmentManager.beginTransaction() .attach(navHostFragment) .apply { if (isPrimaryNavFragment) { setPrimaryNavigationFragment(navHostFragment) } } .commitNow() } private fun obtainNavHostFragment( fragmentManager: FragmentManager, fragmentTag: String, navGraphId: Int, containerId: Int ): NavHostFragment { // If the Nav Host fragment exists, return it val existingFragment = fragmentManager.findFragmentByTag(fragmentTag) as NavHostFragment? existingFragment?.let { return it } // Otherwise, create it and return it. val navHostFragment = NavHostFragment.create(navGraphId) fragmentManager.beginTransaction() .add(containerId, navHostFragment, fragmentTag) .commitNow() return navHostFragment } private fun FragmentManager.isOnBackStack(backStackName: String): Boolean { val backStackCount = backStackEntryCount for (index in 0 until backStackCount) { if (getBackStackEntryAt(index).name == backStackName) { return true } } return false } private fun getFragmentTag(index: Int) = "bottomNavigation#$index" ``` 4. 最终实现效果如下(PS:所有的icon来自<span class="external-link"><a href="https://www.iconfont.cn/" target="_blank">阿里巴巴矢量库<i data-feather='external-link'></i></a></span>): <img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAAA1JREFUCJljOHz4cAMAB2ACypfyMOEAAAAASUVORK5CYII=" data-original="https://s1.ax1x.com/2020/08/03/aNbJTx.gif" alt="效果" /> ***注意事项*** 1. 方法1与方法2的不同点: * 重写BottomNavigationView.setupWithNavController * Menu的Item Id对应绑定的是navigation下的fragment id还是navigation的布局id。 2. 先留个坑。 ## 最后 以上就是本次Jetpack Navigation的实践记录,<span class="external-link"><a href="https://github.com/litchiny/JetPackDemo" target="_blank">源码地址<i data-feather='external-link'></i></a></span>(PS:实现方式1与实现方式2请根据对应的提交日志查看相关代码) 个人精力有限,如果有不正确的地方,欢迎指正,不胜感激。 下一章解析Jetpack Navigation源码。 Last modification:August 3rd, 2020 at 10:13 am © 允许规范转载 Support If you think my article is useful to you, please feel free to appreciate ×Close Appreciate the author Sweeping payments