我是新手,正在开发一个 Android 项目,使用 Hilt 进行依赖注入,使用导航组件进行导航。我有一个 MainActivity 作为我的主机和两个片段:HomeFragment 和 DetailFragment。
当我从 HomeFragment 导航到 DetailFragment 时,导航流程工作正常。但是,当我尝试向后导航时,屏幕变为空白,并且没有显示任何片段。
主要活动
`@AndroidEntryPoint
class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
@Inject
lateinit var featuresNavigation: FeaturesNavigation
private val viewModel: MainViewModel by viewModels()
override fun getVM(): MainViewModel = viewModel
private lateinit var startDestination: Pair<Int, NavArgument?>
override val layoutId = R.layout.activity_main
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val navHostFragment =
supportFragmentManager.findFragmentById(R.id.nav_host) as NavHostFragment
featuresNavigation.bind(navHostFragment)
with(intent) {
initGraph(this)
data = null
replaceExtras(Bundle())
}
}
private fun initGraph(intent: Intent?) {
startDestination = createStartDestination(intent)
if (startDestination.first == R.id.homeFragment) {
Timber.e("MainAct test initGraph")
featuresNavigation.setGraph(
R.navigation.main_navigation,
startDestination.first,
startDestination.second
)
}
}
private fun createStartDestination(intent: Intent?): Pair<Int, NavArgument?> {
val bundle = intent?.getBundleExtra(Constants.KeyParam.KEY_EXTRAS)
return when (intent?.action) {
else -> {
R.id.homeFragment to null
}
}
}
}`
ActivityMain.xml
`<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<FrameLayout
android:id="@+id/layout_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/white">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="com.usmh.libraries.navigationcomponent.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
tools:context=".ui.activity.MainActivity" />
</FrameLayout>
</layout>`
首页片段
`@AndroidEntryPoint
class HomeFragment :
BaseFragment<FragmentHomeBinding, HomeViewModel>(R.layout.fragment_home) {
@Inject
lateinit var featuresNavigation: FeaturesNavigation
private val viewModel: HomeViewModel by viewModels()
override fun getVM() = viewModel
override fun initView(savedInstanceState: Bundle?) {
super.initView(savedInstanceState)
activity?.window?.setBackgroundDrawable(null)
featuresNavigation.openDetail()
}
}`
细节片段
`@AndroidEntryPoint
class DetailFragment :
BaseFragment<FragmentDetailBinding, DetailViewModel>(R.layout.fragment_detail) {
@Inject
lateinit var featuresNavigation: FeaturesNavigation
private val viewModel: DetailViewModel by viewModels()
override fun getVM() = viewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// TODO: Use the ViewModel
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return inflater.inflate(R.layout.fragment_detail, container, false)
}
override fun onBackPressed() {
featuresNavigation.navigateUp()
}
}`
AppNavigatorImpl
`@ActivityScoped
class AppNavigatorImpl @Inject constructor() : BaseNavigatorImpl(), FeaturesNavigation {
override fun openDetail(bundle: Bundle?) {
openScreen(R.id.action_homeFragment_to_detailFragment, bundle, R.id.detailFragment)
}
}`
功能导航
interface FeaturesNavigation : BaseNavigator {
fun openDetail(bundle: Bundle? = null)
}
main_navigation.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"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_navigation"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.usmh.features.ui.HomeFragment"
android:label="HomeFragment"
tools:layout="@layout/fragment_home">
<action
android:id="@+id/action_homeFragment_to_detailFragment"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.usmh.features.ui.DetailFragment"
android:label="DetailFragment"
tools:layout="@layout/fragment_detail" />
</navigation>`
NavHostFramgent
public open class NavHostFragment : Fragment(), NavHost {
private var navHostController: NavHostController? = null
private var isPrimaryBeforeOnCreate: Boolean? = null
private var viewParent: View? = null
// State that will be saved and restored
private var graphId = 0
private var defaultNavHost = false
final override val navController: NavController
get() {
checkNotNull(navHostController) { "NavController is not available before onCreate()" }
return navHostController as NavHostController
}
@CallSuper
public override fun onAttach(context: Context) {
super.onAttach(context)
if (defaultNavHost) {
parentFragmentManager.beginTransaction()
.setPrimaryNavigationFragment(this)
.commit()
}
}
@CallSuper
public override fun onCreate(savedInstanceState: Bundle?) {
var context = requireContext()
navHostController = NavHostController(context)
navHostController!!.setLifecycleOwner(this)
while (context is ContextWrapper) {
if (context is OnBackPressedDispatcherOwner) {
navHostController!!.setOnBackPressedDispatcher(
(context as OnBackPressedDispatcherOwner).onBackPressedDispatcher
)
// Otherwise, caller must register a dispatcher on the controller explicitly
// by overriding onCreateNavHostController()
break
}
context = context.baseContext
}
navHostController!!.enableOnBackPressed(
isPrimaryBeforeOnCreate != null && isPrimaryBeforeOnCreate as Boolean
)
isPrimaryBeforeOnCreate = null
navHostController!!.setViewModelStore(viewModelStore)
onCreateNavHostController(navHostController!!)
var navState: Bundle? = null
if (savedInstanceState != null) {
navState = savedInstanceState.getBundle(KEY_NAV_CONTROLLER_STATE)
if (savedInstanceState.getBoolean(KEY_DEFAULT_NAV_HOST, false)) {
defaultNavHost = true
parentFragmentManager.beginTransaction()
.setPrimaryNavigationFragment(this)
.commit()
}
graphId = savedInstanceState.getInt(KEY_GRAPH_ID)
}
if (navState != null) {
// Navigation controller state overrides arguments
navHostController!!.restoreState(navState)
}
if (graphId != 0) {
// Set from onInflate()
navHostController!!.setGraph(graphId)
} else {
// See if it was set by NavHostFragment.create()
val args = arguments
val graphId = args?.getInt(KEY_GRAPH_ID) ?: 0
val startDestinationArgs = args?.getBundle(KEY_START_DESTINATION_ARGS)
if (graphId != 0) {
navHostController!!.setGraph(graphId, startDestinationArgs)
}
}
super.onCreate(savedInstanceState)
}
@CallSuper
protected open fun onCreateNavHostController(navHostController: NavHostController) {
onCreateNavController(navHostController)
}
@CallSuper
protected open fun onCreateNavController(navController: NavController) {
navController.navigatorProvider +=
DialogFragmentNavigator(requireContext(), childFragmentManager)
navController.navigatorProvider.addNavigator(createFragmentNavigator())
}
@CallSuper
public override fun onPrimaryNavigationFragmentChanged(isPrimaryNavigationFragment: Boolean) {
if (navHostController != null) {
navHostController?.enableOnBackPressed(isPrimaryNavigationFragment)
} else {
isPrimaryBeforeOnCreate = isPrimaryNavigationFragment
}
}
protected open fun createFragmentNavigator(): Navigator<out FragmentNavigator.Destination> {
return FragmentNavigator(requireContext(), childFragmentManager, containerId)
}
public override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val containerView = FragmentContainerView(inflater.context)
// When added via XML, this has no effect (since this FragmentContainerView is given the ID
// automatically), but this ensures that the View exists as part of this Fragment's View
// hierarchy in cases where the NavHostFragment is added programmatically as is required
// for child fragment transactions
containerView.id = containerId
return containerView
}
private val containerId: Int
get() {
val id = id
return if (id != 0 && id != View.NO_ID) {
id
} else R.id.nav_host_fragment_container
// Fallback to using our own ID if this Fragment wasn't added via
// add(containerViewId, Fragment)
}
public override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
check(view is ViewGroup) { "created host view $view is not a ViewGroup" }
Navigation.setViewNavController(view, navHostController)
if (view.getParent() != null) {
viewParent = view.getParent() as View
if (viewParent!!.id == id) {
Navigation.setViewNavController(viewParent!!, navHostController)
}
}
}
@CallSuper
public override fun onInflate(
context: Context,
attrs: AttributeSet,
savedInstanceState: Bundle?
) {
super.onInflate(context, attrs, savedInstanceState)
context.obtainStyledAttributes(
attrs,
androidx.navigation.R.styleable.NavHost
).use { navHost ->
val graphId = navHost.getResourceId(
androidx.navigation.R.styleable.NavHost_navGraph, 0
)
if (graphId != 0) {
this.graphId = graphId
}
}
context.obtainStyledAttributes(attrs, R.styleable.NavHostFragment).use { array ->
val defaultHost = array.getBoolean(R.styleable.NavHostFragment_defaultNavHost, false)
if (defaultHost) {
defaultNavHost = true
}
}
}
@CallSuper
public override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
val navState = navHostController!!.saveState()
if (navState != null) {
outState.putBundle(KEY_NAV_CONTROLLER_STATE, navState)
}
if (defaultNavHost) {
outState.putBoolean(KEY_DEFAULT_NAV_HOST, true)
}
if (graphId != 0) {
outState.putInt(KEY_GRAPH_ID, graphId)
}
}
public override fun onDestroyView() {
super.onDestroyView()
viewParent?.let { view ->
if (Navigation.findNavController(view) === navHostController) {
Navigation.setViewNavController(view, null)
}
}
viewParent = null
}
public companion object {
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public const val KEY_GRAPH_ID: String = "android-support-nav:fragment:graphId"
/**
* @hide
*/
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public const val KEY_START_DESTINATION_ARGS: String =
"android-support-nav:fragment:startDestinationArgs"
private const val KEY_NAV_CONTROLLER_STATE =
"android-support-nav:fragment:navControllerState"
private const val KEY_DEFAULT_NAV_HOST = "android-support-nav:fragment:defaultHost"
@JvmStatic
public fun findNavController(fragment: Fragment): NavController {
var findFragment: Fragment? = fragment
while (findFragment != null) {
if (findFragment is NavHostFragment) {
return findFragment.navHostController as NavController
}
val primaryNavFragment = findFragment.parentFragmentManager
.primaryNavigationFragment
if (primaryNavFragment is NavHostFragment) {
return primaryNavFragment.navHostController as NavController
}
findFragment = findFragment.parentFragment
}
// Try looking for one associated with the view instead, if applicable
val view = fragment.view
if (view != null) {
return Navigation.findNavController(view)
}
// For DialogFragments, look at the dialog's decor view
val dialogDecorView = (fragment as? DialogFragment)?.dialog?.window?.decorView
if (dialogDecorView != null) {
return Navigation.findNavController(dialogDecorView)
}
throw IllegalStateException("Fragment $fragment does not have a navigationComponent.NavController set")
}
@JvmOverloads
@JvmStatic
public fun create(
@NavigationRes graphResId: Int,
startDestinationArgs: Bundle? = null
): NavHostFragment {
var b: Bundle? = null
if (graphResId != 0) {
b = Bundle()
b.putInt(KEY_GRAPH_ID, graphResId)
}
if (startDestinationArgs != null) {
if (b == null) {
b = Bundle()
}
b.putBundle(KEY_START_DESTINATION_ARGS, startDestinationArgs)
}
val result = NavHostFragment()
if (b != null) {
result.arguments = b
}
return result
}
}
}
您的
ActivityMain.xml
文件似乎正在使用 com.usmh.libraries.navigationcomponent.NavHostFragment
。从您发布的代码来看,不需要自定义 NavHostFragment,您应该切换回导航库提供的常规 NavHostFragment。