Android BottomNavigation设置startDestination致Fragment onCreate无限循环
问题现象
开发Android应用时,使用FragmentContainerView+BottomNavigationView配合NavGraph实现底部导航,切换三个仅显示文本的Fragment时遇到两个问题:
- 配置
app:navGraph并设置startDestination后,起始Fragment的onCreate方法被无限循环调用; - 不配置
app:navGraph时,底部导航可见但点击无任何切换效果。
相关代码
MainActivity.kt
class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController } }
FirstFragment.kt(SecondFragment、ThirdFragment逻辑一致)
class FirstFragment: Fragment(R.layout.fragment_first) { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Log.i("FirstFragment", "onCreate called") } }
activity_main.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" android:orientation="horizontal"> <androidx.fragment.app.FragmentContainerView android:id="@+id/nav_host_fragment" android:name="androidx.navigation.fragment.NavHostFragment" app:navGraph="@navigation/simple_nav_graph" android:layout_width="match_parent" android:layout_height="match_parent" app:defaultNavHost="true" app:layout_constraintBottom_toTopOf="@+id/bottom_nav" 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:itemTextColor="@color/cardview_light_background" app:layout_constraintBottom_toBottomOf="@+id/nav_host_fragment" app:menu="@menu/menu_bottom_nav" /> </androidx.constraintlayout.widget.ConstraintLayout>
fragment_first.xml
<?xml version="1.0" encoding="utf-8"?> <fragment xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/details_page_fragment_one" android:name="com.****.****.ui.fragone.FirstFragment" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello Fragment One" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </fragment>
simple_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/simple_nav_graph" app:startDestination="@id/details_page_fragment_one"> <fragment android:id="@+id/details_page_fragment_one" android:name="com.*.*.ui.fragone.FirstFragment" android:label="Fragment 1" tools:layout="@layout/fragment_first"> <action android:id="@+id/action_details_page_fragment_one_to_details_page_fragment_two" app:destination="@id/details_page_fragment_two" /> <action android:id="@+id/action_details_page_fragment_one_to_details_page_fragment_three" app:destination="@id/details_page_fragment_three" /> </fragment> <fragment android:id="@+id/details_page_fragment_two" android:name="com.*.*.ui.fragtwo.SecondFragment" android:label="Fragment 2" tools:layout="@layout/fragment_second"> <action android:id="@+id/action_details_page_fragment_two_to_details_page_fragment_three" app:destination="@id/details_page_fragment_three" /> <action android:id="@+id/action_details_page_fragment_two_to_details_page_fragment_one" app:destination="@id/details_page_fragment_one" /> </fragment> <fragment android:id="@+id/details_page_fragment_three" android:name="com.*.*.ui.fragthree.ThirdFragment" android:label="Fragment 3" tools:layout="@layout/fragment_third"> <action android:id="@+id/action_details_page_fragment_three_to_details_page_fragment_two" app:destination="@id/details_page_fragment_two" /> <action android:id="@+id/action_details_page_fragment_three_to_details_page_fragment_one" app:destination="@id/details_page_fragment_one" /> </fragment> </navigation>
Logcat输出
2023-06-07 16:40:33.457 22883-22883 FirstFragment com...r.automotivecafeapp I onCreate called 2023-06-07 16:40:33.468 22883-22883 FirstFragment com...r.automotivecafeapp I onCreate called ...(重复调用日志)
问题原因
- 无限循环创建Fragment:
fragment_first.xml的根布局使用了<fragment>标签,NavHostFragment会根据导航图创建FirstFragment实例,加载该布局时系统会再次创建FirstFragment,形成无限递归,导致onCreate被反复调用。 - 底部导航无功能:未将
BottomNavigationView与NavController绑定,点击菜单时没有触发导航动作。
解决方案
1. 修改Fragment布局根标签
将所有Fragment布局的根标签从<fragment>改为普通ViewGroup(如ConstraintLayout),避免重复实例化Fragment:
修改后的fragment_first.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" android:layout_width="match_parent" android:layout_height="match_parent"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello Fragment One" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>
2. 绑定BottomNavigationView与NavController
在MainActivity中添加代码,关联底部导航和导航控制器,实现点击切换:
修改后的MainActivity.kt:
class MainActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment val navController = navHostFragment.navController // 绑定底部导航与导航控制器 findViewById<BottomNavigationView>(R.id.bottom_nav).setupWithNavController(navController) } }
3. 简化导航图
底部导航切换无需定义action,NavController会自动匹配菜单id与Fragment id,删除导航图中所有action:
修改后的simple_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/simple_nav_graph" app:startDestination="@id/details_page_fragment_one"> <fragment android:id="@+id/details_page_fragment_one" android:name="com.*.*.ui.fragone.FirstFragment" android:label="Fragment 1" tools:layout="@layout/fragment_first" /> <fragment android:id="@+id/details_page_fragment_two" android:name="com.*.*.ui.fragtwo.SecondFragment" android:label="Fragment 2" tools:layout="@layout/fragment_second" /> <fragment android:id="@+id/details_page_fragment_three" android:name="com.*.*.ui.fragthree.ThirdFragment" android:label="Fragment 3" tools:layout="@layout/fragment_third" /> </navigation>
4. 确保菜单id与Fragment id一致
menu_bottom_nav.xml中每个item的id必须和导航图中对应Fragment的id相同:
示例menu_bottom_nav.xml:
<?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/details_page_fragment_one" android:title="Fragment 1" /> <item android:id="@+id/details_page_fragment_two" android:title="Fragment 2" /> <item android:id="@+id/details_page_fragment_three" android:title="Fragment 3" /> </menu>
内容的提问来源于stack exchange,提问作者snux_cs
相关产品推荐
相关产品推荐

