I'm using the Actionbar
and I have an icon in the appbar which when clicked should go to CategoryFragment
. But I want to make it so that if that fragment is already on the backstack, it should either remove it and then add a new instance of CategoryFragment
to the backstack or replace it.
I tried the code below, but it does not work and only overlays/superimposes another copy of CategoryFragment
on top of the existing CategoryFragment
and also produces very odd and unexpected behaviors. I'm not using the navcontroller to navigate to CategoryFragment
since it adds another copy of the fragment in the backstack. How can I make this work?
onOptionsItemSelected() in Main Activity:
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
}
Main Activity
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
}
Navgraph:
<?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>
You're using NavController
and therefore you should not be doing any manual FragmentTransaction
s at all.
Instead, you should Navigation to a destination, in this case, by using Navigation's support for popUpTo to pop your CategoryFragment off the stack.
Assuming you have a destination set up in your navigation XML for your CategoryFragment
with code such as:
<fragment
android:id="@+id/categoryFragment"
android:name=".CategoryFragment/>
You can create an action that includes the popUpTo
flag you need (you'd put this directly below the <fragment>
itself:
<action
android:id+"@+id/open_category"
app:popUpTo="@id/categoryFragment"
app:popUpToInclusive="true"
app:destination="@id/categoryFragment"/>
This says:
Create an action named open_category
When you trigger this action, pop the stack back up to categoryFragment
if it already exists on the back stack (otherwise, this does nothing). The popUpToInclusive
means that the previous instance is popped.
After you pop the previous instance, create a new instance of categoryFragment
and add it to your back stack.
Then you can trigger that action from your 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)
}
}
}