I am using a ViewPager having 2 pages. First page of that ViewPager is MainFragment. MainFragment has ViewPager as bottomNavigationView
. Each page has a FragmentContainerView
and By default they contains HomeFragment, CommunityFragment and NotificationFragment respectively.
This is the Source Code and this is the APK of the project. So you can test it and improve it easily.
Now if i am in HomeFragment and I click on a profile button so It transact to ProfileFragment and from there setting and so on. And on clicking on back button it get back perfectly one-by-one. But it does not happens same with other FragmentContainerView
. Even they get back directly to the parent fragment. Overall i am unable to handle the backstack between different ViewPagers and fragments.
To avoid the confusion of FragmentContainers
i transact it like this
val containerId = (view?.parent as ViewGroup).id
activity?.supportFragmentManager?.beginTransaction()?.add(containerId, profileFragment)?.addToBackStack(null)?.commit()
Now the handling of BackPressed() in MainActivity is here
if (view_pager_main_activity?.currentItem == 0)
{
if (view_pager_main_fragment?.currentItem == 0)
{
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view_home)
val appbarHome = findViewById<AppBarLayout>(R.id.appbar_home)
val layoutManager = recyclerView?.layoutManager as LinearLayoutManager
when {
layoutManager.findFirstCompletelyVisibleItemPosition() == 0 -> {
super.onBackPressed()
}
supportFragmentManager.backStackEntryCount != 0 -> {
supportFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
}
else -> {
layoutManager.scrollToPositionWithOffset(0, 0)
appbarHome.setExpanded(true)
//recyclerView.smoothScrollToPosition(0)
}
}
}
else
{
// Otherwise, select the previous step.
view_pager_main_fragment?.setCurrentItem(view_pager_main_fragment.currentItem - 1, false)
}
}
else
{
// Otherwise, select the previous step.
view_pager_main_activity?.currentItem = view_pager_main_activity.currentItem - 1
}
I checked your code and the problem was the usage of
supportFragmentManager.popBackStack(null,FragmentManager.POP_BACK_STACK_INCLUSIVE)
If you read the description of the flag POP_BACK_STACK_INCLUSIVE
it says:
If set, and the name or ID of a back stack entry has been supplied, then all matching entries will be consumed until one that doesn't match is found or the bottom of the stack is reached. Otherwise, all entries up to but not including that entry will be removed.
So it was removing the multiple entries at once.
The reason it worked for Home screen was that it never reached that case. It always went in the first case of:
layoutManager.findFirstCompletelyVisibleItemPosition() == 0 -> {
super.onBackPressed()
}
And that's what you exactly need for your case just call super.onBackPressed()
when you want to pop only one fragment at once. So your final code becomes like this:
In your MainActivity's onBackPressed()
override fun onBackPressed() {
if (view_pager_main_activity?.currentItem == 0) {
if (view_pager_main_fragment?.currentItem == 0) {
val recyclerView = findViewById<RecyclerView>(R.id.listRecyclerView)
val appbarHome = findViewById<AppBarLayout>(R.id.appbar_home)
val layoutManager = recyclerView?.layoutManager as LinearLayoutManager
when {
layoutManager.findFirstCompletelyVisibleItemPosition() == 0 -> {
super.onBackPressed()
}
//This is never reached. It can be removed
supportFragmentManager.backStackEntryCount != 0 -> {
supportFragmentManager.popBackStack(
null,
FragmentManager.POP_BACK_STACK_INCLUSIVE
)
}
else -> {
layoutManager.scrollToPositionWithOffset(0, 0)
appbarHome.setExpanded(true)
//recyclerView.smoothScrollToPosition(0)
}
}
} else if (supportFragmentManager.backStackEntryCount != 0) {
super.onBackPressed()
} else {
// Otherwise, select the previous step.
view_pager_main_fragment?.setCurrentItem(
view_pager_main_fragment.currentItem - 1,
false
)
}
} else if (supportFragmentManager.backStackEntryCount != 0) {
super.onBackPressed()
} else {
// Otherwise, select the previous step.
view_pager_main_activity?.currentItem = view_pager_main_activity.currentItem - 1
}
}
Edit 1: From the comments
I want to clear the backstack when bottom nav selection changes.
If you want to clear the selection when you change the bottom navigation selection changes then you've to set a listener and clear the back stack in your MainFragment
as following:
view?.bottomNavView?.setOnNavigationItemSelectedListener { item ->
//check if item is being re-selected then just return and don't do anything
if (bottomNavView.selectedItemId == item.itemId){
return@setOnNavigationItemSelectedListener false
}
//if the bottom nav item selection changes then clear the back stack of previous tab
parentFragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE)
when (item.itemId) {
R.id.homeFragment -> viewPager2.setCurrentItem(0, false)
R.id.searchFragment -> viewPager2.setCurrentItem(1, false)
R.id.notificationsFragment -> viewPager2.setCurrentItem(2, false)
}
true
}
And the rest can remain same. Try this out and do let me know.