Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save smihajlovski/fa53e60a4df1dd20eecfa24735ecc67c to your computer and use it in GitHub Desktop.
Save smihajlovski/fa53e60a4df1dd20eecfa24735ecc67c to your computer and use it in GitHub Desktop.
public class MainActivity extends AppCompatActivity implements FragmentInteractionCallback {
private Map<String, Stack<String>> tagStacks;
private String currentTab;
private ActivityMainBinding binder;
private List<String> stackList;
private List<String> menuStacks;
private Fragment currentFragment;
private Fragment homeFragment;
private Fragment dashboardFragment;
private Fragment notificationFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binder = DataBindingUtil.setContentView(this, R.layout.activity_main);
createStacks();
}
@Override
public void onFragmentInteractionCallback(Bundle bundle) {
String action = bundle.getString(ACTION);
if (action != null) {
switch (action) {
case HomeFragment.ACTION_DASHBOARD:
showFragment(bundle, DashboardFragment.newInstance(false));
break;
case DashboardFragment.ACTION_NOTIFICATION:
showFragment(bundle, NotificationsFragment.newInstance(false));
break;
case NotificationsFragment.ACTION_DASHBOARD:
showFragment(bundle, DashboardFragment.newInstance(false));
break;
}
}
}
@Override
public void onBackPressed() {
resolveBackPressed();
}
private void createStacks() {
binder.bottomNavigationView.inflateMenu(R.menu.bottom_nav_tabs);
binder.bottomNavigationView.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener);
homeFragment = HomeFragment.newInstance(true);
dashboardFragment = DashboardFragment.newInstance(true);
notificationFragment = NotificationsFragment.newInstance(true);
tagStacks = new LinkedHashMap<>();
tagStacks.put(TAB_HOME, new Stack<>());
tagStacks.put(TAB_DASHBOARD, new Stack<>());
tagStacks.put(TAB_NOTIFICATIONS, new Stack<>());
menuStacks = new ArrayList<>();
menuStacks.add(TAB_HOME);
stackList = new ArrayList<>();
stackList.add(TAB_HOME);
stackList.add(TAB_DASHBOARD);
stackList.add(TAB_NOTIFICATIONS);
binder.bottomNavigationView.setSelectedItemId(R.id.tab_home);
binder.bottomNavigationView.setOnNavigationItemReselectedListener(onNavigationItemReselectedListener);
}
private final BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemSelectedListener = item -> {
switch (item.getItemId()) {
case R.id.tab_home:
selectedTab(TAB_HOME);
return true;
case R.id.tab_dashboard:
selectedTab(TAB_DASHBOARD);
return true;
case R.id.tab_notifications:
selectedTab(TAB_NOTIFICATIONS);
return true;
}
return false;
};
private final BottomNavigationView.OnNavigationItemReselectedListener onNavigationItemReselectedListener = menuItem -> {
switch (menuItem.getItemId()) {
case R.id.tab_home:
popStackExceptFirst();
break;
case R.id.tab_dashboard:
popStackExceptFirst();
break;
case R.id.tab_notifications:
popStackExceptFirst();
break;
}
};
private void selectedTab(String tabId) {
currentTab = tabId;
BaseFragment.setCurrentTab(currentTab);
if (tagStacks.get(tabId).size() == 0) {
/*
First time this tab is selected. So add first fragment of that tab.
We are adding a new fragment which is not present in stack. So add to stack is true.
*/
switch (tabId) {
case TAB_HOME:
addInitialTabFragment(getSupportFragmentManager(), tagStacks, TAB_HOME, homeFragment, R.id.frame_layout, true);
resolveStackLists(tabId);
assignCurrentFragment(homeFragment);
break;
case TAB_DASHBOARD:
addAdditionalTabFragment(getSupportFragmentManager(), tagStacks, TAB_DASHBOARD, dashboardFragment, currentFragment, R.id.frame_layout, true);
resolveStackLists(tabId);
assignCurrentFragment(dashboardFragment);
break;
case TAB_NOTIFICATIONS:
addAdditionalTabFragment(getSupportFragmentManager(), tagStacks, TAB_NOTIFICATIONS, notificationFragment, currentFragment, R.id.frame_layout, true);
resolveStackLists(tabId);
assignCurrentFragment(notificationFragment);
break;
}
} else {
/*
* We are switching tabs, and target tab already has at least one fragment.
* Show the target fragment
*/
Fragment targetFragment = getSupportFragmentManager().findFragmentByTag(tagStacks.get(tabId).lastElement());
showHideTabFragment(getSupportFragmentManager(), targetFragment, currentFragment);
resolveStackLists(tabId);
assignCurrentFragment(targetFragment);
}
}
private void popFragment() {
/*
* Select the second last fragment in current tab's stack,
* which will be shown after the fragment transaction given below
*/
String fragmentTag = tagStacks.get(currentTab).elementAt(tagStacks.get(currentTab).size() - 2);
Fragment fragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);
/*pop current fragment from stack */
tagStacks.get(currentTab).pop();
removeFragment(getSupportFragmentManager(), fragment, currentFragment);
assignCurrentFragment(fragment);
}
private void resolveBackPressed() {
int stackValue = 0;
if (tagStacks.get(currentTab).size() == 1) {
Stack<String> value = tagStacks.get(stackList.get(1));
if (value.size() > 1) {
stackValue = value.size();
popAndNavigateToPreviousMenu();
}
if (stackValue <= 1) {
if (menuStacks.size() > 1) {
navigateToPreviousMenu();
} else {
finish();
}
}
} else {
popFragment();
}
}
/*Pops the last fragment inside particular tab and goes to the second tab in the stack*/
private void popAndNavigateToPreviousMenu() {
String tempCurrent = stackList.get(0);
currentTab = stackList.get(1);
BaseFragment.setCurrentTab(currentTab);
binder.bottomNavigationView.setSelectedItemId(resolveTabPositions(currentTab));
Fragment targetFragment = getSupportFragmentManager().findFragmentByTag(tagStacks.get(currentTab).lastElement());
showHideTabFragment(getSupportFragmentManager(), targetFragment, currentFragment);
assignCurrentFragment(targetFragment);
updateStackToIndexFirst(stackList, tempCurrent);
menuStacks.remove(0);
}
private void navigateToPreviousMenu() {
menuStacks.remove(0);
currentTab = menuStacks.get(0);
BaseFragment.setCurrentTab(currentTab);
binder.bottomNavigationView.setSelectedItemId(resolveTabPositions(currentTab));
Fragment targetFragment = getSupportFragmentManager().findFragmentByTag(tagStacks.get(currentTab).lastElement());
showHideTabFragment(getSupportFragmentManager(), targetFragment, currentFragment);
assignCurrentFragment(targetFragment);
}
private void popStackExceptFirst() {
if (tagStacks.get(currentTab).size() == 1) {
return;
}
while (!tagStacks.get(currentTab).empty()
&& !getSupportFragmentManager().findFragmentByTag(tagStacks.get(currentTab).peek()).getArguments().getBoolean(EXTRA_IS_ROOT_FRAGMENT)) {
getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentByTag(tagStacks.get(currentTab).peek()));
tagStacks.get(currentTab).pop();
}
Fragment fragment = getSupportFragmentManager().findFragmentByTag(tagStacks.get(currentTab).elementAt(0));
removeFragment(getSupportFragmentManager(), fragment, currentFragment);
assignCurrentFragment(fragment);
}
/*
* Add a fragment to the stack of a particular tab
*/
private void showFragment(Bundle bundle, Fragment fragmentToAdd) {
String tab = bundle.getString(DATA_KEY_1);
boolean shouldAdd = bundle.getBoolean(DATA_KEY_2);
addShowHideFragment(getSupportFragmentManager(), tagStacks, tab, fragmentToAdd, getCurrentFragmentFromShownStack(), R.id.frame_layout, shouldAdd);
assignCurrentFragment(fragmentToAdd);
}
private int resolveTabPositions(String currentTab) {
int tabIndex = 0;
switch (currentTab) {
case TAB_HOME:
tabIndex = R.id.tab_home;
break;
case TAB_DASHBOARD:
tabIndex = R.id.tab_dashboard;
break;
case TAB_NOTIFICATIONS:
tabIndex = R.id.tab_notifications;
break;
}
return tabIndex;
}
private void resolveStackLists(String tabId) {
updateStackIndex(stackList, tabId);
updateTabStackIndex(menuStacks, tabId);
}
private Fragment getCurrentFragmentFromShownStack() {
return getSupportFragmentManager().findFragmentByTag(tagStacks.get(currentTab).elementAt(tagStacks.get(currentTab).size() - 1));
}
private void assignCurrentFragment(Fragment current) {
currentFragment = current;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment