Я конвертирую свое приложение для использования одного действия и добавляю BottomnavigationView, а также пытаюсь предотвратить воссоздание фрагментов, делая ненужные вызовы api при навигации между фрагментами. Однако я не могу заставить его работать:
- Фрагмент не отображается
- Иконки не переключаются
- Касание пунктов нижнего меню не переключает фрагменты
- Прикосновение к выбранному элементу приводит к сбою приложения с исключением TypeCastException:
kotlin.TypeCastException: null cannot be cast to non-null type androidx.navigation.fragment.NavHostFragment at com.example.testapp.NavigationExtensionsKt$setupItemReselected$1.onNavigationItemReselected
Макет действия:
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="@+id/nav_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/nav_view"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="0dp"
android:layout_marginStart="0dp"
app:menu="@menu/bottom_nav_menu" />
Меню BottomNav:
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/navigation_1"
android:icon="@drawable/ic_1"
android:title="@string/title_1" />
<item
android:id="@+id/homeFragment"
android:icon="@drawable/ic_2"
android:title="@string/home" />
<item
android:id="@+id/navigation_3"
android:icon="@drawable/ic_3"
android:title="@string/title_3" />
</menu>
В MainActivity:
class MainActivity : DaggerAppCompatActivity() {
private var currentNavController: LiveData<NavController>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
nav_view.itemIconTintList = null
if(savedInstanceState == null){
setupBottomNavigationBar()
} // Else, need to wait for onRestoreInstanceState
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
// Now that BottomNavigationBar has restored its instance state
// and its selectedItemId, we can proceed with setting up the
// BottomNavigationBar with Navigation
setupBottomNavigationBar()
}
private fun setupBottomNavigationBar() {
val bottomNavigationView = findViewById<BottomNavigationView>(R.id.nav_view)
val navGraphIds = listOf(R.navigation.mobile_navigation, R.navigation.settings)
val menuItemsIds = listOf(R.id.navigation_1, R.id.homeFragment, R.id.navigation_3)
val defaultIconsIds = listOf(R.drawable.ic_1, R.drawable.ic_2, R.drawable.ic_3)
val selectedIconsIds = listOf(R.drawable.ic_1a, R.drawable.ic_2a, R.drawable.ic_3a)
// Setup the bottom navigation view with a list of navigation graphs (custom extension)
val controller = bottomNavigationView.setupWithNavController(
navGraphIds = navGraphIds,
menuItemsIds = menuItemsIds,
defaultIconsIds = defaultIconsIds,
selectedIconsIds = selectedIconsIds,
fragmentManager = supportFragmentManager,
containerId = R.id.nav_host_container,
intent = intent
)
currentNavController = controller
// updating icons
nav_view.menu.iterator().forEach {
if (it.isChecked) {
it.setIcon(selectedIconsIds[menuItemsIds.indexOf(it.itemId)])
} else {
it.setIcon(defaultIconsIds[menuItemsIds.indexOf(it.itemId)])
}
}
}
override fun onSupportNavigateUp(): Boolean {
return currentNavController?.value?.navigateUp() ?: false
}
}
NavigationExtensions:
//.....
//....
private fun BottomNavigationView.setupItemReselected(
menuItemsIds: List<Int>,
defaultIconsIds: List<Int>,
selectedIconsIds: List<Int>,
graphIdToTagMap: SparseArray<String>,
fragmentManager: FragmentManager) {
setOnNavigationItemReselectedListener { item ->
this.menu.iterator().forEach { it ->
it.setIcon(defaultIconsIds[menuItemsIds.indexOf(it.itemId)])
}
item.setIcon(selectedIconsIds[menuItemsIds.indexOf(item.itemId)])
val newlySelectedItemTag = graphIdToTagMap[item.itemId]
val selectedFragment = fragmentManager.findFragmentByTag(newlySelectedItemTag) as NavHostFragment //TypeCastException happens here
val navController = selectedFragment.navController
// Pop the back stack to the start destination of the current navController graph
navController.popBackStack(
navController.graph.startDestination, false
)
}
}
Экран пустой без визуализации фрагментов.
Кто-нибудь может помочь мне исправить это? Я что-то упустил?
PS: Код взят из приложения с открытым исходным кодом, которое можно найти здесь
[Edit] Добавление NavGraph:
мобильная_навигация
<fragment
android:id="@+id/homeFragment"
android:name="com.example.testapp.homeFragment"
android:label="@string/home"
tools:layout="@layout/home_fragment" >
<action
android:id="@+id/homrFragment_to_detailsFragment"
app:launchSingleTop="true"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:destination="@id/detailsFragment" />
</fragment>
<fragment
android:id="@+id/navigation_1"
android:name="com.example.testapp.Fragment_S"
android:label="@string/fragment_s"
tools:layout="@layout/fragment_s" >
<action
android:id="@+id/action_navigation_1_to_detailsFragment"
app:destination="@id/detailsFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_in_left" />
</fragment>
<fragment
android:id="@+id/navigation_3"
android:name="com.example.testapp.Fragment3"
android:label="@string/fragment_ss"
tools:layout="@layout/fragment_ss" />
<dialog
android:id="@+id/detailsFragment"
android:name="com.example.testapp.DetailsFragment"
android:label="InDetails"
tools:layout="@layout/layout_details">
<argument
android:name="arg1"
android:defaultValue=""
app:argType="string"/>
<argument
android:name="arg2"
android:defaultValue=""
app:argType="string" />
</dialog>
Настройки навигации:
<?xml version="1.0" encoding="utf-8"?>