android childFragmentManager.findFragmentById(R.id.nav_host_fragment_challenges)返回null

kyvafyod  于 2023-05-15  发布在  Android
关注(0)|答案(1)|浏览(87)

感谢 bottomNavigationView,我使用NavController在4个不同的片段之间导航。
在其中一个片段(ChallengesFragment)中,我有一个inner_fragment(nav_host_fragment_challenges),但是当我尝试在这个inner_fragment中导航时,它不起作用。
下面是我的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(承载inner_fragment的fragment):

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不能强制转换为非null类型androidx.navigation.fragment.NavHostFragment

所以我尝试了val challengesNavHostFragment = requireActivity().supportFragmentManager.findFragmentById(R.id.nav_host_fragment_challenges) as NavHostFragment(代码中带有//的行),它一开始工作,但当我离开 ChallengesFragment 然后回来时,inner_fragment不再显示...
我认为应该使用childFragmentManager,但如果你有一个解决方案可以与其他东西一起使用,我会接受它。
我的fragment_challenges.xmlFragmentContainerView

<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是如何工作的,因为正如你所看到的,我有点迷失了方向……谢谢
91zkwejq

91zkwejq1#

传入onCreateViewinflater不是LayoutInflater.from(context)得到的LayoutInflater-它知道你在哪个片段中。这就是FragmentContainerView如何知道它需要被添加为您所在的Fragment的子片段(如果您查看source code,您将看到FragmentContainerView的特殊大小写)。
所以当你使用

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

您正在创建一个完全不知道您所在片段的LayoutInflater。这就是为什么你的片段(应该是一个子片段)实际上被添加到Activity的FragmentManager中。这样做的副作用是,实际上并没有将片段嵌套在另一个片段中,从而导致当父片段再次可见时,片段不会重新出现(因为从FragmentManager的Angular 来看,它们是完全独立的片段)。
如果您专门为片段启用了StrictMode,这类问题将触发WrongNestedHierarchyViolation,因为没有正确嵌套片段会产生负面影响。
因此,您应该:
1.使用onCreateView传递给您的inflater
1.在fragment_challenges.xml XML文件中使用android:theme="@style/Theme.challenges"为层次结构中该部分的所有内容设置主题,而不是使用ContextThemeWrapper

相关问题