Separate Back Stack For Each Tab in Android Using Fragments

Separate Back Stack for each tab in Android using Fragments

The framework won't currently do this for you automatically. You will need to build and manage your own back stacks for each tab.

To be honest, this seems like a really questionable thing to do. I can't imagine it resulting in a decent UI -- if the back key is going to do different things depending on the tab I am, especially if the back key also has its normal behavior of closing the entire activity when at the top of the stack... sounds nasty.

If you are trying to build something like a web browser UI, to get a UX that is natural to the user is going to involve a lot of subtle tweaks of behavior depending on context, so you'll definitely need to do your own back stack management rather than rely on some default implementation in the framework. For an example try paying attention to how the back key interacts with the standard browser in the various ways you can go in and out of it. (Each "window" in the browser is essentially a tab.)

Separate Back Stack for each tab in BottomNavigationView Android using Fragments

Finally, I found the solution, it was inspired by a previous answer on StackOverflow: Separate Back Stack for each tab in Android using Fragments

I only have replaced TabHost with BottomNavigationView and here is the code:

Main Activity

public class MainActivity extends AppCompatActivity {

private HashMap<String, Stack<Fragment>> mStacks;
public static final String TAB_HOME = "tab_home";
public static final String TAB_DASHBOARD = "tab_dashboard";
public static final String TAB_NOTIFICATIONS = "tab_notifications";

private String mCurrentTab;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

mStacks = new HashMap<String, Stack<Fragment>>();
mStacks.put(TAB_HOME, new Stack<Fragment>());
mStacks.put(TAB_DASHBOARD, new Stack<Fragment>());
mStacks.put(TAB_NOTIFICATIONS, new Stack<Fragment>());

navigation.setSelectedItemId(R.id.navigation_home);
}

private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {

@Override
public boolean onNavigationItemSelected(@NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
selectedTab(TAB_HOME);
return true;
case R.id.navigation_dashboard:
selectedTab(TAB_DASHBOARD);
return true;
case R.id.navigation_notifications:
selectedTab(TAB_NOTIFICATIONS);
return true;
}
return false;
}

};

private void gotoFragment(Fragment selectedFragment)
{
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content, selectedFragment);
fragmentTransaction.commit();
}

private void selectedTab(String tabId)
{
mCurrentTab = tabId;

if(mStacks.get(tabId).size() == 0){
/*
* First time this tab is selected. So add first fragment of that tab.
* Dont need animation, so that argument is false.
* We are adding a new fragment which is not present in stack. So add to stack is true.
*/
if(tabId.equals(TAB_HOME)){
pushFragments(tabId, new HomeFragment(),true);
}else if(tabId.equals(TAB_DASHBOARD)){
pushFragments(tabId, new DashboardFragment(),true);
}else if(tabId.equals(TAB_NOTIFICATIONS)){
pushFragments(tabId, new NotificationsFragment(),true);
}
}else {
/*
* We are switching tabs, and target tab is already has atleast one fragment.
* No need of animation, no need of stack pushing. Just show the target fragment
*/
pushFragments(tabId, mStacks.get(tabId).lastElement(),false);
}
}

public void pushFragments(String tag, Fragment fragment, boolean shouldAdd){
if(shouldAdd)
mStacks.get(tag).push(fragment);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}

public void popFragments(){
/*
* Select the second last fragment in current tab's stack..
* which will be shown after the fragment transaction given below
*/
Fragment fragment = mStacks.get(mCurrentTab).elementAt(mStacks.get(mCurrentTab).size() - 2);

/*pop current fragment from stack.. */
mStacks.get(mCurrentTab).pop();

/* We have the target fragment in hand.. Just show it.. Show a standard navigation animation*/
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(R.id.content, fragment);
ft.commit();
}

@Override
public void onBackPressed() {
if(mStacks.get(mCurrentTab).size() == 1){
// We are already showing first fragment of current tab, so when back pressed, we will finish this activity..
finish();
return;
}

/* Goto previous fragment in navigation stack of this tab */
popFragments();
}

}

Home fragment example

public class HomeFragment extends Fragment {
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
Button gotoNextFragment = (Button) view.findViewById(R.id.gotoHome2);

gotoNextFragment.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
((MainActivity)getActivity()).pushFragments(MainActivity.TAB_HOME, new Home2Fragment(),true);
}
});
return view;
}

}

Maintain separate backstack for each tab (implemented with fragments)

Actually you can not. The backstack is unique for every Activity in which your fragments run. So the only way, IMO, is to have more Activity.

Edit: see my question here

Maintain separate backstack for each tab (implemented with fragments)

Actually you can not. The backstack is unique for every Activity in which your fragments run. So the only way, IMO, is to have more Activity.

Edit: see my question here

How to make separate back stack for a fragment in Android

Thanks for all. There are a lot of answers here (ZERO). It is "so complicated" question dudes!

I figured out that it is possible to realize this functionality using Fragment.getChildFragmentManager with frameLayout inside Category fragment. Switching between categories using category fragments; Making separate back stack with .getChildFragmentManager and frameLayout inside category fragments with sub category fragments. Also you can user ViewPager as a switching tool.



Related Topics



Leave a reply



Submit