Search code examples
javaandroidxml

Change BottomNavigationView Icons on Back Button clicked


At the bottom of my layout I have a BottomNavigationView with three fragments. If I click the back button the fragment is switching but not the bottom icons. How can I fix it?

addToBackStack() works. Maybe you have some advise to pretty the code.

Is it good practise to have the fragment tags in the activity or fragment?

public class MainActivity extends AppCompatActivity {

    private FragmentManager mFragmentManager;
    private BottomNavigationView mBottomNavigationView;
    private static final String HOME_FRAGMENT = "homeFragment";
    private static final String SEARCH_FRAGMENT = "searchFragment";
    private static final String SHARE_FRAGMENT = "shareFragment";
    private boolean isFirstFragment;
    private long mBackPressedTime;

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

            init();
            setBottomNavigationView();
        }

        private void init() {
            mBottomNavigationView = findViewById(R.id.bottomNavigationView);

            mFragmentManager = getSupportFragmentManager();
            mBackPressedTime = 0;
        }

        private void setBottomNavigationView() {
            setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
            isFirstFragment = true;
            mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    switch (item.getItemId()) {
                        case R.id.ic_home:
                            setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
                            return true;
                        case R.id.ic_search:
                            setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
                            return true;
                        case R.id.ic_circle:
                            setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
                            return true;

                        default:
                            return false;
                    }
                }
            });
        }

        private void setFragment(Fragment fragment, String tag) {
            FragmentTransaction transaction = mFragmentManager.beginTransaction();
            transaction.replace(R.id.container, fragment, tag);
            if (isFirstFragment) {
                transaction.addToBackStack(tag);
            }
            transaction.commit();
        }

        @Override
        public void onBackPressed() {
            final long currentTimeMillis = System.currentTimeMillis();
            if (mFragmentManager.getBackStackEntryCount() > 0) {
                mFragmentManager.popBackStack();
            } else if (currentTimeMillis - mBackPressedTime > 2000) {
                mBackPressedTime = currentTimeMillis;
                Toast.makeText(this, getString(R.string.reach_homescreen), Toast.LENGTH_SHORT).show();
            } else {
                super.onBackPressed();
            }
        }
    }

Solution

  • @Hans Baum instead of adding your first fragment to back stack try this code,

        @Override
        public void onBackPressed() {
            if(mBottomNavigationView.getSelectedItemId () != R.id.ic_home)
            {
                mBottomNavigationView.setSelectedItemId(R.id.ic_home);
            }
            else
            {
                super.onBackPressed();
            }     
        }
    

    This code will exit your activity if you are in Home Fragment else if you are in any other Fragment it will go to Home Fragment. So no addToBackStack() needed. So your serFragment() method should be,

      private void setFragment(Fragment fragment, String tag) {
            FragmentTransaction transaction = mFragmentManager.beginTransaction();
            transaction.replace(R.id.container, fragment, tag);
            transaction.commit();
        }
    

    hope it helps. Note that in your code you never assigned false to isFirstFragment so i assume all fragments are added to backstack which is very memory consuming.

    UPDATE: Since your requirement is different.As you want Instagram like tabs, i hope this implementation helps you .

        Deque<Integer> mStack = new ArrayDeque<>();
        boolean isBackPressed  = false;
    
        private void setBottomNavigationView() {
            mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
                @Override
                public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                    switch (item.getItemId()) {
                        case R.id.ic_home:
                             if(!isBackPressed) {
                               pushFragmentIntoStack(R.id.ic_home);
                           }
                            isBackPressed = false
                            setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
                            return true;
                        case R.id.ic_search:
                            if(!isBackPressed) {
                               pushFragmentIntoStack(R.id.ic_search);
                           }
                            isBackPressed = false
                            setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
                            return true;
                        case R.id.ic_circle:
                            if(!isBackPressed) {
                               pushFragmentIntoStack(R.id.ic_circle);
                           }
                            isBackPressed = false
                            setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
                            return true;
    
                        default:
                            return false;
                    }
                }
            });
            mBottomNavigationView.setOnNavigationItemReselectedListener(new 
                  BottomNavigationView.OnNavigationItemReselectedListener() {
                @Override
                public void onNavigationItemReselected(@NonNull MenuItem item) {
    
                }
            });
         mBottomNavigationView.setSelectedItemId(R.id.ic_home);
         pushFragmentIntoStack(R.id.ic_home);
        }
    
        private void pushFragmentIntoStack(int id)
        {
            if(mStack.size() < 3)
            {
                mStack.push(id);
            }
            else
            {
                mStack.removeLast();
                mStack.push(id);
            }
        }
    
        private void setFragment(Fragment fragment, String tag) {
            FragmentTransaction transaction = mFragmentManager.beginTransaction();
            transaction.replace(R.id.container, fragment, tag);
            transaction.commit();
        }
    
        @Override
        public void onBackPressed() {
            if(mStack.size() > 1)
            {
                isBackPressed = true;
                mStack.pop();
                mBottomNavigationView.setSelectedItemId(mStack.peek());
            }
            else 
            {
                super.onBackPressed();
            }
        }
    

    I have used deque to store the order in which the tabs are clicked Since there are 3 tabs deque size is 3 .On back press it will pop the stack and go to that tab. If no item in stack it will quit the activity.

    IMPORTANT NOTE: Do not add fragments to backstack in this scenario because as i switch the tabs the backstack count will keep increasing and may even lead to MemoryOutOfBound exception.

    To Question 2: Coming to your Fragment tag question , It is good to save tag in fragment . As the fragment is reusable in any activity instead adding tag in every activity it is used , it is good if we add it in fragment itself.