英文:
Multiple versions of BottomNavigationView
问题
我想要两个版本的BottomNavigationView,每个版本有自己的标签集。有些是共享的,有些不是。例如,版本1在底部有Fragment A、B、C、D,版本2在底部有Fragment B、C、E、F。我想要根据用户的类型来加载视图和起始目的地。理想情况下,我的流程应该是:
- 获取用户或提示登录
- 根据用户,加载版本1并选择起始目的地A,或者加载版本2并选择起始目的地F。
- 每当显示版本1时,它需要弹出到目的地A;每当显示版本2时,它需要弹出到目的地F。
实现此导航堆栈的最佳方法是什么?显然有共享目的地,但也有时候版本1不应该最终显示到E和F片段,而版本2也不应该意外看到A和D片段。
我已经在主活动中尝试了以下代码,但效果很笨拙。很多时候当用户向上导航时,我们会短暂地向用户显示错误的版本。
我正在使用Kotlin和MVVM,因此activity_main具有以下内容:
And in the Main activity I've added all of these clunks of code:
这使得体验非常笨拙,每当用户停留在today/home片段时,我们就会重新加载。很多时候,可能会向用户显示错误的起始目的地,然后才显示正确的目的地。在不重复太多代码的情况下,如何最好地处理应用程序的结构,并在不同类型的用户之间切换时进行处理?谢谢
英文:
I would like to have two versions of the BottomNavigationView, each with its own set of tabs. Some are shared, some not. For example, Version 1 has Fragment A, B, C, D on the bottom, version two has Fragment B, C, E, F on the bottom. I would like to inflate the view and the start destination based on the type of the user. So ideally my flow would be:
- fetch the user, or prompt log in
- depending on the user, inflate version 1 with start destination A, or version 2 with start destination F.
- whenever the version 1 pops up, it needs to pop up to destination A, whenever version 2 pops up it needs to pop up to destination F.
What is the best way to implement this navigation stack. There are obviously shared destinations, but there are also times when version 1 should never end up on fragments E, and F, and version 2 should never accidentally see fragments A and D.
I have tried the following code in main activity, but that works very clunky. A lot of times when the user navigates up, for a split second we show a wrong version to the user.
I'm using Kotlin with MVVM, so activity_main has the following:
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_gravity="bottom"
app:itemBackground="?attr/colorSurface"
app:labelVisibilityMode="labeled">
And in the Main activity I've added all of these clunks of code:
override fun onNavigateUp(): Boolean {
Timber.e("navigate up: ${navController?.currentDestination?.id}")
Timber.e("navigate up: ${navController?.previousBackStackEntry?.id}")
return super.onNavigateUp()
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Hide the default menu options to prevent duplication.
return false
}
private fun inflateBottomNavigation(user: User) {
if (isXUser(user)) {
binding.bottomNavigationView.inflateMenu(R.menu.menu_bottom_nav_x)
navController?.navigateSafely(
R.id.home_fragment) // Set the top fragment to HomeFragment.
navController?.graph?.setStartDestination(R.id.home_fragment)
} else {
binding.bottomNavigationView.inflateMenu(R.menu.menu_bottom_nav_y)
navController?.navigateSafely(
R.id.today_fragment) // Set the top fragment to TodayFragment.
navController?.graph?.setStartDestination(R.id.today_fragment)
}
// Set up bottom navigation.
binding.bottomNavigationView.setupWithNavController(navController!!)
binding.bottomNavigationView.setOnItemSelectedListener { item ->
when (item.itemId) {
R.id.home_fragment,
R.id.today_fragment -> {
if (isXUser(user)) {
navController?.navigateSafely(R.id.home_fragment)
} else {
navController?.navigateSafely(R.id.today_fragment)
}
true
}
R.id.chat_fragment,
R.id.chat_x_fragment -> {
if (isXUser(user)) {
navController?.navigateSafely(R.id.chat_x_fragment)
} else {
navController?.navigateSafely(R.id.chat_fragment)
}
true
}
else -> {
navController?.navigateSafely(item.itemId)
true
}
}
}
}
private fun isXUser(user: User): Boolean {
return user.isDebugger == true
}
private fun setupNavigation() {
navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host_fragment_container)
as NavHostFragment
navController = navHostFragment?.navController
appBarConfiguration =
AppBarConfiguration(
setOf(
R.id.home_fragment,
R.id.today_fragment,
R.id.test_fragment,
R.id.chat_fragment,
R.id.chat_x_fragment,
R.id.progress_fragment,
R.id.points_fragment),
binding.drawerLayout)
binding.toolbar.setupWithNavController(navController!!, appBarConfiguration!!)
binding.drawerNavView.setupWithNavController(navController!!)
...
// Set up bottom navigation.
binding.bottomNavigationView.setupWithNavController(navController!!)
}
private fun setUpUserObservers() {
userViewModel.user.observe(
this,
Observer { user ->
inflateBottomNavigation(user)
}
This makes the experience very clunky, each time the user ends on the today/home fragment we re-inflate. A lot of times a wrong start destination can be shown to a user before the correct one shows. What is the best approach to structure the app, and handle switching between different types of users, without duplicating too much code? Thanks
答案1
得分: 1
我建议你在导航目录中创建一个 navGraph 文件,如果你不知道如何做,可以在这里参考:https://developer.android.com/guide/navigation/navigation-getting-started。
要实现 navGraph,在 activity_main.xml 中,你需要在 BottomNavGraph 上面添加 FragmentContainerView。
<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" />
然后在主活动中,我们会根据用户类型以编程方式更改 navGraph 的 startDestination。我认为为不同的情况创建两个不同的菜单文件会更简单。
这是主活动代码:
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
private lateinit var bottomNavigationView: BottomNavigationView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
val navGraph = navController.navInflater.inflate(R.navigation.nav_graph)
val userType = fetchUserType()
val startDestination = if (userType == UserType.TYPE1) {
R.id.fragmentA
} else {
R.id.fragmentF
}
navGraph.startDestination = startDestination
navController.graph = navGraph
bottomNavigationView = findViewById(R.id.bottom_navigation_view)
if (userType == UserType.TYPE1) {
bottomNavigationView.inflateMenu(R.menu.menu_version1)
} else {
bottomNavigationView.inflateMenu(R.menu.menu_version2)
}
bottomNavigationView.setupWithNavController(navController)
}
<details>
<summary>英文:</summary>
I would suggest you to create a navGraph file in the navigation directory , if you don't know how to do that you can refer it here https://developer.android.com/guide/navigation/navigation-getting-started.
And to implement the navGraph, in the acitivy_main.xml you would have to add FragmentContainerView above the BottomNavGraph.
<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" />
Then in the main activity we would programmatically change the startDestination of the navGraph based on the userType.
And I think creating two different menu files for different would be simpler.
Here's the main activity code
class MainActivity : AppCompatActivity() {
private lateinit var navController: NavController
private lateinit var bottomNavigationView: BottomNavigationView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
navController = navHostFragment.navController
val navGraph = navController.navInflater.inflate(R.navigation.nav_graph)
val userType = fetchUserType()
val startDestination = if (userType == UserType.TYPE1) {
R.id.fragmentA
} else {
R.id.fragmentF
}
navGraph.startDestination = startDestination
navController.graph = navGraph
bottomNavigationView = findViewById(R.id.bottom_navigation_view)
if (userType == UserType.TYPE1) {
bottomNavigationView.inflateMenu(R.menu.menu_version1)
} else {
bottomNavigationView.inflateMenu(R.menu.menu_version2)
}
bottomNavigationView.setupWithNavController(navController)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论