替换或删除后台堆栈上现有片段的代码不起作用

Posted

技术标签:

【中文标题】替换或删除后台堆栈上现有片段的代码不起作用【英文标题】:Code to replace or remove existing fragment on backstack not working 【发布时间】:2020-05-19 11:41:06 【问题描述】:

我正在使用Actionbar,并且我在应用栏中有一个图标,单击该图标应转到CategoryFragment。但我想这样做,如果该片段已经在后台堆栈上,它应该删除它,然后将 CategoryFragment 的新实例添加到后台堆栈或替换它。

我尝试了下面的代码,但它不起作用,只会在现有的CategoryFragment 之上覆盖/叠加CategoryFragment 的另一个副本,并且还会产生非常奇怪和意外的行为。我没有使用导航控制器导航到CategoryFragment,因为它在后台堆栈中添加了片段的另一个副本。我怎样才能做到这一点?

主 Activity 中的 onOptionsItemSelected():

override fun onOptionsItemSelected(item: MenuItem): Boolean 

        Log.i("Lifecycle-Activity", "OnOptionsItemSelected() called")

         when (item.itemId) 
                android.R.id.home -> 
                        onBackPressed()
                        return true
                

               R.id.categoryFragment -> 

                    var backStateName = CategoryFragment::class.simpleName

                    var fragmentPopped = supportFragmentManager.popBackStackImmediate(backStateName, 0)

                    if(!fragmentPopped)
                    
                        var ft = supportFragmentManager.beginTransaction()

                        //also tried R.id.main_activity_container which is not working
                        ft.replace(R.id.navHostFragment, CategoryFragment())

                        ft.addToBackStack(backStateName)
                        ft.commit()
                    

                    return true
                
    return true
    

主要活动

  class MainActivity : AppCompatActivity() 

        private lateinit var appBarConfiguration: AppBarConfiguration

        private lateinit var navController: NavController

        override fun onCreate(savedInstanceState: Bundle?) 
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main_activity)

            Log.i("Lifecycle-Activity", "OnCreate() called")

            navController = Navigation.findNavController(this, R.id.navHostFragment)

            NavigationUI.setupActionBarWithNavController(this, navController)

            appBarConfiguration = AppBarConfiguration.Builder(navController.graph)
                .build()
        

        override fun onSupportNavigateUp(): Boolean 

    super.onSupportNavigateUp()

            return NavigationUI.navigateUp(navController, appBarConfiguration)

        

        override fun onCreateOptionsMenu(menu: Menu?): Boolean 

            Log.i("Lifecycle-Activity", "OnCreateOptionsMenu() called")

            menuInflater.inflate(R.menu.main_menu, menu)
            return super.onCreateOptionsMenu(menu)
        

        override fun onOptionsItemSelected(item: MenuItem): Boolean 

            Log.i("Lifecycle-Activity", "OnOptionsItemSelected() called")
 when (item.itemId) 
            android.R.id.home -> 
                    onBackPressed()
                    return true
            

  R.id.categoryFragment -> 

                var backStateName = CategoryFragment::class.simpleName

                var fragmentPopped = supportFragmentManager.popBackStackImmediate(backStateName, 0)

                if(!fragmentPopped)
                
                    var ft = supportFragmentManager.beginTransaction()

                    //also tried R.id.main_activity_container which is not working
                    ft.replace(R.id.navHostFragment, CategoryFragment())

                    ft.addToBackStack(backStateName)
                    ft.commit()
                

            

           return true
        
return true

导航图:

<?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_main"
    app:startDestination="@id/categoriesFragment">
    <fragment
        android:id="@+id/clockFragment"
        android:name="com.example.pomoplay.ui.main.ClockFragment"
        android:label="Pomo Clock"
        tools:layout="@layout/fragment_clock" />
    <fragment
        android:id="@+id/categoryFragment"
        android:name="com.example.pomoplay.ui.main.CategoryFragment"
        android:label="Category"
        tools:layout="@layout/fragment_category">
        <action
            android:id="@+id/action_open_category_fragment"
            app:destination="@id/categoryFragment"
            app:popUpTo="@id/categoryFragment"
            app:popUpToInclusive="true" />
        <action
            android:id="@+id/action_categoryFragment_to_clockFragment"
            app:destination="@id/clockFragment" />
        <action
            android:id="@+id/action_categoryFragment_to_newTaskDialogFragment"
            app:destination="@id/newTaskDialogFragment" />
        <argument
            android:name="category"
            app:argType="com.example.pomoplay.Category" />
    </fragment>
    <fragment
        android:id="@+id/categoriesFragment"
        android:name="com.example.pomoplay.ui.main.CategoriesFragment"
        android:label="Categories"
        tools:layout="@layout/fragment_categories">
        <action
            android:id="@+id/action_categoriesFragment_to_newCategoryDialogFragment"
            app:destination="@id/newCategoryDialogFragment" />
        <argument
            android:name="category"
            app:argType="com.example.pomoplay.Category" />
        <action
            android:id="@+id/action_categoriesFragment_to_categoryFragment"
            app:destination="@id/categoryFragment" />
        <argument
            android:name="fromNewCategoryDialog"
            app:argType="boolean"
            android:defaultValue="false" />
    </fragment>
    <dialog
        android:id="@+id/newCategoryDialogFragment"
        android:name="com.example.pomoplay.ui.main.NewCategoryDialogFragment"
        tools:layout="@layout/fragment_new_category_dialog">
        <action
            android:id="@+id/action_newCategoryDialogFragment_to_categoriesFragment"
            app:destination="@id/categoriesFragment"
            app:popUpToInclusive="false" />
    </dialog>
    <dialog
        android:id="@+id/newTaskDialogFragment"
        android:name="com.example.pomoplay.ui.main.NewTaskDialogFragment"
        android:label="fragment_new_task_dialog"
        tools:layout="@layout/fragment_new_task_dialog" >
        <action
            android:id="@+id/action_newTaskDialogFragment_to_categoryFragment"
            app:destination="@id/categoryFragment"
            app:popUpTo="@+id/categoryFragment"
            app:popUpToInclusive="true" />
    </dialog>
</navigation>

【问题讨论】:

【参考方案1】:

您正在使用NavController,因此您根本不应该手动操作FragmentTransactions。

相反,您应该 Navigation to a destination,在这种情况下,通过使用 Navigation 对 popUpTo 的支持将您的 CategoryFragment 从堆栈中弹出。

假设您在导航 XML 中为您的CategoryFragment 设置了一个目的地,代码如下:

<fragment
    android:id="@+id/categoryFragment"
    android:name=".CategoryFragment/>

您可以 create an action 包含您需要的 popUpTo 标志(您可以将其直接放在 &lt;fragment&gt; 本身下方:

<action
    android:id+"@+id/open_category"
    app:popUpTo="@id/categoryFragment"
    app:popUpToInclusive="true"
    app:destination="@id/categoryFragment"/>

这说:

    创建一个名为open_category的操作

    当您触发此操作时,如果堆栈已存在于后面的堆栈中,则将堆栈弹出回至 categoryFragment(否则,这不会执行任何操作)。 popUpToInclusive 表示之前的实例被弹出。

    弹出前一个实例后,创建categoryFragment 的新实例并将其添加到您的后台堆栈。

然后您可以从您的onOptionsItemSelected()' 触发该操作:

override fun onOptionsItemSelected(item: MenuItem): Boolean 

    Log.i("Lifecycle-Activity", "OnOptionsItemSelected() called")

    return when (item.itemId) 
        R.id.categoryFragment -> 
            // Trigger the action
            navController.navigate(R.id.open_category);
            true
       
       default -> 
           // android.R.id.home is handled by the call to super,
           // you do not need to handle it here.
           super.onOptionsItemSelected(item)
       
    

【讨论】:

我完全按照您的说明进行操作,但是当我单击应用栏菜单图标时,应用程序崩溃并显示以下错误消息:navigation destination com.example.pomoplay:id/action_open_category_fragment is unknown to this NavController。可能是什么问题? 您的解决方案似乎仅在当前可见片段为CategoryFragment 时才有效。如果当前可见片段是不同的片段,则单击 ActionBar 中的菜单图标会导致应用程序崩溃并显示以下错误消息:navigation destination com.example.pomoplay:id/action_open_category_fragment is unknown to this NavController 似乎您将操作 within 放在 &lt;fragment&gt; 标记中,而不是在其下方。他们应该在同一级别 我就是这样。我把动作放在它下面。在&lt;fragment&gt;/&gt; 结束标记之后。但是,它仍然会因该错误消息而崩溃。 能否在问题中包含导航 XML?

以上是关于替换或删除后台堆栈上现有片段的代码不起作用的主要内容,如果未能解决你的问题,请参考以下文章

Android从后台堆栈中删除事务

无法替换图像视图,用于来自相机或画廊的图像(它不起作用)

从片段替换片段时,onRequestPermissionsResult 回调不起作用

学说迁移:向现有表添加和删除两个外键列不起作用

java代码在片段活动中不起作用

phpMyAdmin CSV 上传替换数据不起作用