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 для фрагментов специально из-за негативных побочных эффектов неправильного вложения фрагментов.
Итак, вам следует:
- Использовать
inflater
прошел к вам мимоonCreateView
- Использовать
android:theme="@style/Theme.challenges"
в вашейfragment_challenges.xml
XML-файл, чтобы установить тему для всего в этой части иерархии, вместо использованияContextThemeWrapper
.