Просмотр фрагментов с помощью findNavController

#java #android #android-studio #kotlin

Вопрос:

Я использую навигационный компонент Android с нижней навигацией, lint выдает предупреждение о замене <fragment> тега, <FragmentContainerView> но когда я его заменил, findNavController он не работает, это выдает мне ошибку о том, что в нем не установлен навигационный контроллер

Фрагмент

 <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/mobile_navigation" />
 

Активность

 val navController = findNavController(R.id.nav_host_fragment)
    
    val appBarConfiguration = AppBarConfiguration(
        setOf(
            R.id.navigation_classes, R.id.navigation_schedule, R.id.navigation_settings
        )
    )
    setupActionBarWithNavController(navController, appBarConfiguration)
    navView.setupWithNavController(navController)
}
 

Комментарии:

1. заменить FragmentContainerView на fragment

2. Я не хочу использовать fragment , есть предупреждение, если я заменю

Ответ №1:

В соответствии с этой проблемой при использовании FragmentContainerView вам нужно найти NavController использование findFragmentById() , а не использовать findNavController() , когда в onCreate() :

 val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
 

Это связано с тем, что findNavController(R.id.nav_host_fragment) полагается на то, что представление фрагмента уже создано, чего не происходит при использовании FragmentContainerView (так как FragmentTransaction для добавления используется под капотом NavHostFragment ).

Если вы используете фрагмент 1.4.0 или выше и просматриваете привязку, вы можете значительно упростить это, используя getFragment() метод:

 val navController = binding.container.getFragment<NavHostFragment>().navController
 

Комментарии:

1. Да, за пределами onCreate() представления фрагмента будет создан, так что по-прежнему будет нормально звонить findNavController()

2. Этот ответ не сработал внутри Fragment.onCreate() . Говорит NavController is not available before onCreate() .

3. Если вы находитесь во фрагменте, вы бы здесь ничего не использовали, вы бы использовали NavHostFragment.findNavController(this) / findNavController() .

4. @AdamHurwitz — да, NavigationAdvancedSample он использует полностью независимые NavHostFragment экземпляры для поддержки нескольких обратных стеков в соответствии с описанием примера ; он использует совершенно другой стиль и функциональность, отличные от любой документации.

5. @Chuck — это не «волшебная переменная» — так код Kotlin ссылается на getSupportFragmentManager() API FragmentActivity .

Ответ №2:

Замените эту строку:

 NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
 

с

 NavController navController = getNavController();
 

Где getNavController() выглядит так:

     // workaround for https://issuetracker.google.com/issues/142847973
    @NonNull
    private NavController getNavController() {
        Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
        if (!(fragment instanceof NavHostFragment)) {
            throw new IllegalStateException("Activity "   this
                      " does not have a NavHostFragment");
        }
        return ((NavHostFragment) fragment).getNavController();
    }
 

Комментарии:

1. для меня это лучшее решение

Ответ №3:

В дополнение к принятому ответу можно было бы использовать небольшой короткий путь:

 supportFragmentManager.findFragmentById(R.id.navHostMain)?.findNavController()
 

С уважением

Ответ №4:

Добавьте в свой build.gradle (Модуль: Приложение) эту строку

 implementation "androidx.navigation:navigation-fragment-ktx:2.3.2"
 

и используйте это в своей деятельности

 val navController = supportFragmentManager.findFragmentById(R.id.your_id_nav_host_fragment)
            ?.findNavController()
 

и в отрывке

 val navController = findNavController()
 

Ответ №5:

Я решил свою проблему с расширением:

 fun FragmentActivity.findFragmentContainerNavController(@IdRes host: Int):NavController {
    try {
        val navHostFragment = supportFragmentManager.findFragmentById(host) as NavHostFragment
        return navHostFragment.findNavController()
    } catch (e: Exception) {
        throw IllegalStateException("Activity $this does not have a NavController set on $host")
    }
}
 

Ответ №6:

Есть еще более простой способ ответить @friederbluemle. Проверки утверждений должно быть достаточно.

 NavHostFragment navHostFragment 
    = (NavHostFragment) getSupportFragmentManager().findFragmentById(R.id.nav_host_fragment);
assert navHostFragment != null : "navHostFragment not found";
NavController navController = navHostFragment.getNavController();
 

Ответ №7:

Вот простой ответ —

 val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragmentContainerView)
 

как NavHostFragment

 val navController = navHostFragment.navControllermenus.setupWithNavController(navController)

 

Комментарии:

1. Что это navControllermenus ?

Ответ №8:

используйте простой <fragment> тег вместо <androidx.fragment.app.FragmentContainerView>