后堆栈在 Jetpack Navigation 中无法正常工作

Posted

技术标签:

【中文标题】后堆栈在 Jetpack Navigation 中无法正常工作【英文标题】:Back stack not working properly in Jetpack Navigation 【发布时间】:2021-12-26 22:54:01 【问题描述】:

我创建了一个简单的演示项目来测试 android Jetpack Navigation 组件的默认回栈功能。 我有一个主要活动和两个片段。应用程序运行时正在显示主页片段。主页片段有一个按钮。单击导航到另一个片段。 但是每当我按下系统后退按钮时,我的整个应用程序就完成了,而不是通常的后退堆栈行为,我应该看到主页片段。 任何帮助表示赞赏。提前致谢。 代码如下: MainActivity.kt

package com.callsamik.jetpacknavigationcomponentdemo

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    

activity_main.xml(主要活动的布局)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_
    android:layout_
    tools:context=".MainActivity">

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/nave_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_
        android:layout_
        app:navGraph="@navigation/nav_graph"
        />

</androidx.constraintlayout.widget.ConstraintLayout>

HomeFragment.kt

package com.callsamik.jetpacknavigationcomponentdemo

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.findNavController
import com.callsamik.jetpacknavigationcomponentdemo.databinding.FragmentHomeBinding

class HomeFragment: Fragment() 
    var binding: FragmentHomeBinding? = null
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? 
        binding = FragmentHomeBinding.inflate(layoutInflater)
        return binding?.root
    
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) 
        super.onViewCreated(view, savedInstanceState)
        binding?.loginButton?.setOnClickListener
            val action = HomeFragmentDirections.actionHomeFragmentToLoginFragment()
            findNavController().navigate(action)
        
    

    override fun onDestroyView() 
        super.onDestroyView()
        binding = null
    

fragment_home.xml(HomeFragment 的布局)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_
    android:layout_
    android:gravity="center"
    tools:context= ".HomeFragment">
    <androidx.appcompat.widget.AppCompatTextView
        android:layout_
        android:layout_
        android:layout_marginBottom="16dp"
        android:text="Home Screen"
        android:textAppearance="@style/TextAppearance.AppCompat.Large"/>

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/loginButton"
        android:layout_
        android:layout_
        android:text="Login"/>
</LinearLayout>

LoginFragment.kt(第二个片段)

package com.callsamik.jetpacknavigationcomponentdemo

import androidx.fragment.app.Fragment

class LoginFragment: Fragment(R.layout.fragment_login) 

fragment_login.xml(第二个片段布局)

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:gravity="center"
    android:padding="16dp"
    android:orientation="vertical"
    tools:context=".LoginFragment">

    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/usernameEditText"
        android:layout_
        android:layout_
        android:hint="Username"/>

    <androidx.appcompat.widget.AppCompatEditText
        android:id="@+id/passwordEditText"
        android:layout_
        android:layout_
        android:hint="Password"/>

    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/confirmButton"
        android:text="Confirm"
        android:layout_
        android:layout_/>

</androidx.appcompat.widget.LinearLayoutCompat>

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"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/nav_graph"
    app:startDestination="@id/homeFragment">

    <fragment
        android:id="@+id/homeFragment"
        android:name="com.callsamik.jetpacknavigationcomponentdemo.HomeFragment"
        android:label="fragment_home"
        tools:layout="@layout/fragment_home" >
        <action
            android:id="@+id/action_homeFragment_to_loginFragment"
            app:destination="@id/loginFragment"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />
    </fragment>
    <fragment
        android:id="@+id/loginFragment"
        android:name="com.callsamik.jetpacknavigationcomponentdemo.LoginFragment"
        android:label="fragment_login"
        tools:layout="@layout/fragment_login" />
</navigation>

顶层 build.gradle

// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript 
    ext.kotlin_version = "1.5.31"
    ext.nav_version = "2.3.5"
    repositories 
        google()
        mavenCentral()
    
    dependencies 
        classpath "com.android.tools.build:gradle:7.0.3"
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"

        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    


task clean(type: Delete) 
    delete rootProject.buildDir

应用模块 build.gradle

plugins 
    id 'com.android.application'
    id 'kotlin-android'
    id 'androidx.navigation.safeargs.kotlin'


android 
    compileSdk 31

    defaultConfig 
        applicationId "com.callsamik.jetpacknavigationcomponentdemo"
        minSdk 22
        targetSdk 31
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    

    buildFeatures 
        viewBinding true
    

    buildTypes 
        release 
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        
    
    compileOptions 
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    
    kotlinOptions 
        jvmTarget = '1.8'
    


dependencies 

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
    implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
    implementation "androidx.navigation:navigation-ui-ktx:$nav_version"

    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'

【问题讨论】:

【参考方案1】:

根据the documentation:

app:defaultNavHost="true" 属性确保您的NavHostFragment 拦截系统后退按钮。请注意,只有一个NavHost 可以是默认值。如果您在同一布局中有多个主机(例如,双窗格布局),请务必仅指定一个默认 NavHost

您缺少该属性,因此 Navigation 无法处理系统后退按钮。

<androidx.fragment.app.FragmentContainerView
    android:id="@+id/nave_host_fragment"
    android:name="androidx.navigation.fragment.NavHostFragment"
    android:layout_
    android:layout_
    app:navGraph="@navigation/nav_graph"
    app:defaultNavHost="true"
    />

【讨论】:

以上是关于后堆栈在 Jetpack Navigation 中无法正常工作的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack Navigation、BottomNavigationView 与 Youtube 或 Instagram 类似正确的后退导航(片段后退堆栈)?

Android jetpack之Navigation

Jetpack Navigation 真香预警

如何在 JetPack Navigation 组件中托管 Fragment 的 Activity 中调用导航 Fragments 函数?

Jetpack系列 — Navigation

Android Jetpack Navigation:如何在 OnNavigatedListener 中获取目的地的片段实例?