childFragmentManager.findFragmentById(R.id.nav_host_fragment_challenges) возвращает ноль

Благодаря BottomNavigationView я использую NavController для навигации между четырьмя различными фрагментами.
В одном из этих фрагментов (ChallengesFragment) у меня есть внутренний_фрагмент (nav_host_fragment_challenges), но когда я пытаюсь перемещаться по этому внутреннему_фрагменту, это не работает.

Вот мой MainActivity.kt :

      class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

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

        val bottomNavigationView = findViewById<BottomNavigationView>(R.id.navigation_bar)
        bottomNavigationView.setupWithNavController(navController)
    }
}

Мой ChallengesFragment.kt (фрагмент, содержащий внутренний_фрагмент):

      class ChallengesFragment : Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        val inflater_challenges = LayoutInflater.from(ContextThemeWrapper(context, R.style.Theme_challenges))
        val view = inflater_challenges.inflate(R.layout.fragment_challenges, container, false)

        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val challengesNavHostFragment = childFragmentManager.findFragmentById(R.id.nav_host_fragment_challenges) as NavHostFragment
        //val challengesNavHostFragment = requireActivity().supportFragmentManager.findFragmentById(R.id.nav_host_fragment_challenges) as NavHostFragment
        val challengesNavController = challengesNavHostFragment.navController
    }
}

Моя проблема :val challengesNavHostFragment = childFragmentManager.findFragmentById(R.id.nav_host_fragment_challenges) as NavHostFragmentдайте мне эту ошибку: java.lang.NullPointerException: null не может быть приведен к ненулевому типу androidx.navigation.fragment.NavHostFragment
Итак, я попробовалval challengesNavHostFragment = requireActivity().supportFragmentManager.findFragmentById(R.id.nav_host_fragment_challenges) as NavHostFragment(строка с // в коде), и сначала это работает, но когда я выхожу из ChallengesFragment , а затем возвращаюсь, внутренний_фрагмент больше не отображается...
Я думаю, что лучше всего использовать childFragmentManager, но если у вас есть решение, которое работает с чем-то другим, я так понимаю.

FragmentContainerView моего фрагмента_challenges.xml : _

      <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nav_host_fragment_challenges"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/challenge_topbar"
        app:navGraph="@navigation/nav_graph_challenges"
        app:defaultNavHost="false"
        tools:layout="@layout/fragment_quests" />

Мой 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"
    android:id="@+id/nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/profileFragment"
        android:name="fr.apgames.veuja.fragments.ProfileFragment"
        android:label="ProfileFragment" />
    <fragment
        android:id="@+id/calendarFragment"
        android:name="fr.apgames.veuja.fragments.CalendarFragment"
        android:label="CalendarFragment" />
    <fragment
        android:id="@+id/challengesFragment"
        android:name="fr.apgames.veuja.fragments.ChallengesFragment"
        android:label="ChallengesFragment" />
    <fragment
        android:id="@+id/homeFragment"
        android:name="fr.apgames.veuja.fragments.HomeFragment"
        android:label="HomeFragment" />
    <include app:graph="@navigation/nav_graph_challenges" />
</navigation>

И, наконец, мой nav_graph_challenges.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"
    android:id="@+id/nav_graph_challenges"
    app:startDestination="@id/challengesQuestsFragment">

    <fragment
        android:id="@+id/challengesMyStoryFragment"
        android:name="fr.apgames.veuja.fragments.ChallengesMyStoryFragment"
        android:label="ChallengesMyStoryFragment" >
        <action
            android:id="@+id/action_challengesMyStoryFragment_to_challengesQuestsFragment"
            app:destination="@id/challengesQuestsFragment" />
    </fragment>
    <fragment
        android:id="@+id/challengesQuestsFragment"
        android:name="fr.apgames.veuja.fragments.ChallengesQuestsFragment"
        android:label="ChallengesQuestsFragment" >
        <action
            android:id="@+id/action_challengesQuestsFragment_to_challengesMyStoryFragment"
            app:destination="@id/challengesMyStoryFragment" />
    </fragment>
</navigation>
  • И если вы можете объяснить, как именно работает NavController, потому что, как вы можете видеть, я немного запутался в этом... Спасибо.

1 ответ

Перешел в не то же самоеLayoutInflaterты бы получил отLayoutInflater.from(context)- он знает, в каком фрагменте вы находитесь. Вот откуда он знает, что его нужно добавить как дочерний фрагмент фрагмента, в котором вы находитесь (если вы посмотрите исходный код, вы увидите специальный регистр дляFragmentContainerView).

Поэтому, когда вы используете

      val inflater_challenges = LayoutInflater.from(
    ContextThemeWrapper(context, R.style.Theme_challenges))

Вы создаете LayoutInflater, который совершенно не знает, на каком фрагменте вы находитесь. Вот почему ваш фрагмент (который должен быть дочерним фрагментом) фактически добавляется в Activity.FragmentManager. Это имеет побочный эффект, заключающийся в том, что фрагменты фактически не вложены друг в друга, что приводит к тому, что фрагмент не появляется снова, когда родительский элемент снова становится видимым (поскольку с точки зрения FragmentManager они являются полностью независимыми фрагментами).

Проблема такого рода вызоветWrongNestedHierarchyViolationесли вы включили StrictMode для фрагментов специально из-за негативных побочных эффектов неправильного вложения фрагментов.

Итак, вам следует:

  1. Использоватьinflaterпрошел к вам мимоonCreateView
  2. Использоватьandroid:theme="@style/Theme.challenges"в вашейfragment_challenges.xmlXML-файл, чтобы установить тему для всего в этой части иерархии, вместо использованияContextThemeWrapper.
Другие вопросы по тегам