How to handle bottom navigation perfectly with back pressed
Asked Answered
D

23

34

I am working on a bottom navigation bar, but I am not getting perfectly bottom navigation bar.

My MainActivity class:

public class MainActivity extends AppCompatActivity {

    private static final String SELECTED_ITEM = "selected_item";

    private BottomNavigationView bottomNavigationView;
    private Toolbar toolbar;
    private MenuItem menuItemSelected;
    private int mMenuItemSelected;


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

        toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
        bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(@NonNull MenuItem item) {
                selectFragment(item);
                return true;
            }
        });

        //Always load first fragment as default
        FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
        fragmentTransaction.replace(R.id.frameLayout, new AnnouncementFragment());
        fragmentTransaction.commit();

        if (savedInstanceState != null) {
            mMenuItemSelected = savedInstanceState.getInt(SELECTED_ITEM, 0);
            menuItemSelected = bottomNavigationView.getMenu().findItem(mMenuItemSelected);
        } else {
            menuItemSelected = bottomNavigationView.getMenu().getItem(0);
        }

        selectFragment(menuItemSelected);
    }

    private void selectFragment(MenuItem item) {
        Fragment fragment = null;
        Class fragmentClass;
        switch (item.getItemId()) {
            case R.id.action_announcement:
                fragmentClass = AnnouncementFragment.class;
                break;
            case R.id.action_menu:
                fragmentClass = MenuFragment.class;
                break;
            case R.id.action_menu_reports:
                fragmentClass = ReportFragment.class;
                break;
            case R.id.action_setting:
                fragmentClass = SettingFragment.class;
                break;

            default:
                fragmentClass = AnnouncementFragment.class;
        }

        try {
            fragment = (Fragment) fragmentClass.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        FragmentManager fragmentManager = getSupportFragmentManager();
        fragmentManager.beginTransaction().replace(R.id.frameLayout, fragment).commit();
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putInt(SELECTED_ITEM, mMenuItemSelected);
        super.onSaveInstanceState(outState);
    }

And my back pressed also not working properly:

 @Override
    public void onBackPressed() {
        MenuItem homeItem = bottomNavigationView.getMenu().getItem(0);
        if (mMenuItemSelected != homeItem.getItemId()) {
            selectFragment(homeItem);
        } else {
            super.onBackPressed();
        }
    }

How should I do that because bottom menu has uneven distribution on bar. How to properly maintain the menu space without uneven distribution.

Here I am attaching my result which I obtain on AVD

Dinin answered 9/5, 2017 at 12:58 Comment(3)
What exactly isn't working? Please describe the behavior you are trying to get and the unexpected behavior you actually getting.Myrtie
On BackPressed is not working properly and when I hit setting icon there is enough space between iconDinin
You don't need to use any customization for bottom navigation bar.Bertrand
N
34

According to the guidelines for Material Design

On Android, the Back button does not navigate between bottom navigation bar views.

EDIT: Material Design link no longer mentions back button behavior.

Pressing the back button you can quit the application, which is the default behavior, such as in Google Photo...

FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
// note: there is NOT a addToBackStack call
fragmentTransaction.commit();

...or lead the user to the home section and then, if pushed again, at the exit.

Personally I find this last pattern much better.

To get it without override onBackPressed you need to identify the home fragment and differentiate it from all the others

navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        switch (item.getItemId()) {
            case R.id.navigation_home:
                viewFragment(new HomeFragment(), FRAGMENT_HOME);
                return true;
            case R.id.navigation_page_1:
                viewFragment(new OneFragment(), FRAGMENT_OTHER);
                return true;
            case R.id.navigation_page_2:
                viewFragment(new TwoFragment(), FRAGMENT_OTHER);
                return true;
        }
        return false;
    }
});

What you have to do now is write the viewfragment method that have to:

  1. Know how many fragments there are in the stack before the commit
  2. If the fragment is not "home type", save it to the stack before the commit

  3. Add an OnBackStackChangedListener that when the stack decreases, (i.e. when I pressed back ), delete all the fragments that are not "home type" (POP_BACK_STACK_INCLUSIVE) , bringing us to the home fragment

Below the full method with comments

private void viewFragment(Fragment fragment, String name){
    final FragmentManager fragmentManager = getFragmentManager();
    FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    fragmentTransaction.replace(R.id.content, fragment);
    // 1. Know how many fragments there are in the stack
    final int count = fragmentManager.getBackStackEntryCount();
    // 2. If the fragment is **not** "home type", save it to the stack
    if( name.equals( FRAGMENT_OTHER) ) {
        fragmentTransaction.addToBackStack(name);
    }
    // Commit !
    fragmentTransaction.commit();
    // 3. After the commit, if the fragment is not an "home type" the back stack is changed, triggering the
    // OnBackStackChanged callback
    fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {
            // If the stack decreases it means I clicked the back button
            if( fragmentManager.getBackStackEntryCount() <= count){
                // pop all the fragment and remove the listener
                fragmentManager.popBackStack(FRAGMENT_OTHER, POP_BACK_STACK_INCLUSIVE);
                fragmentManager.removeOnBackStackChangedListener(this);
                // set the home button selected
                navigation.getMenu().getItem(0).setChecked(true);
            }
        }
    });
}
Nealy answered 25/5, 2017 at 21:35 Comment(4)
does not work. it never calls the onBackStackChanged method, despite the fragmentTransaction.addtoBackstack(name) is called...Assignation
adding a onBackStackChange listener every time is a bad practice.Profusion
Not working. Call every time addOnBackStackChangedListener and count is every time 0Deina
Hi @emreturka, this is a 3yo answer, today please refer to developer.android.com/guide/navigation. This solution worked fine at that time, now i haven't tried it anymoreNealy
E
25

Try this

@Override
    public void onBackPressed() {
        BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
        int seletedItemId = bottomNavigationView.getSelectedItemId();
        if (R.id.home != seletedItemId) {
            setHomeItem(MainActivity.this);
        } else {
            super.onBackPressed();
        }
    }

public static void setHomeItem(Activity activity) {
    BottomNavigationView bottomNavigationView = (BottomNavigationView)
            activity.findViewById(R.id.navigation);
    bottomNavigationView.setSelectedItemId(R.id.home);
}
Endometriosis answered 13/7, 2017 at 12:55 Comment(2)
Every time set home fragment.Deina
Wrong solution. super.onBackPressed() does lots of things including dispatching the event to BackPressedDispatchers. You prevent them to execute on bottom fragments that are not the Home one.Underling
I
12
@Override
    public void onBackPressed() {
        BottomNavigationView mBottomNavigationView = findViewById(R.id.navigation);
        if (mBottomNavigationView.getSelectedItemId() == R.id.navigation_home)
        {
            super.onBackPressed();
            finish();
        }
        else
        {
            mBottomNavigationView.setSelectedItemId(R.id.navigation_home);
        }
    }
Irenics answered 8/8, 2018 at 12:15 Comment(1)
This codes set home every back pressed and second pres quit.Deina
K
9

This is maybe a little late but I think the best way to do it is as simple as this.

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

I hope it helps and happy coding :)

Khufu answered 26/12, 2017 at 17:37 Comment(0)
S
8

onBackPressed did not worked for me. So this I used.

 @Override
 protected void onResume() {
    super.onResume();
    bottomNavigationView.getMenu().getItem(0).setChecked(true);
 }
Schuller answered 8/3, 2018 at 10:29 Comment(0)
K
3

The best way is: when in home button the app is closed and when in another button back in home button.

Below I put my code :

first i'm load home button in navigation View :

private void loadFragment(Fragment fragment) {
    Toast.makeText(this, "load", Toast.LENGTH_SHORT).show();
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.frame_container, fragment, TAG_FRAGMENT);
    transaction.commit();
}

and remember dont call addToBackStack() . and then handle the situation by onBackPressed() :

@Override
public void onBackPressed() {
    if (navigation.getSelectedItemId() == R.id.bottomAkhbar) {
        super.onBackPressed();
    } else {
        navigation.setSelectedItemId(R.id.bottomAkhbar);
    }
}
Krall answered 30/8, 2018 at 18:59 Comment(0)
N
2

I had the same problem so I solved my problem using this method: In my main activity, I have a bottom nav bar and a nav drawer, I need to sync items in my drawer and bottom nav:

I created a method for my main fragment and the others: my main fragment replacer:

public void MainFragmentChanger(final Fragment fragment, final String TAG){
    if (main_page_fragment != null){
        fragmentTransaction = myFragmentManager.beginTransaction();
        fragmentTransaction.remove(main_page_fragment).commit();
    }
    if (main_drawer.isDrawerOpen()){
        main_drawer.closeDrawer();
    }
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            main_page_fragment = fragment;
            main_page_fragment.setRetainInstance(true);
            fragmentTransaction = myFragmentManager.beginTransaction();
            fragmentTransaction.replace(R.id.main_container, main_page_fragment,TAG);
            fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            fragmentTransaction.commitAllowingStateLoss();
        }
    });
}

and this is for my other fragment replacer:

public void changeBottomFragment(final Fragment fragment, final String TAG){
    if (main_drawer.isDrawerOpen()){
        main_drawer.closeDrawer();
    }
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            main_page_fragment = fragment;
            main_page_fragment.setRetainInstance(true);
            fragmentTransaction = myFragmentManager.beginTransaction();
            fragmentTransaction.replace(R.id.main_container, main_page_fragment);
            fragmentTransaction.addToBackStack(null);
            fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
            fragmentTransaction.commitAllowingStateLoss();
        }
    });
}

so after that, I need to sync items in both drawer and bar: Note I use material navigation drawer by Mike Penz, and com.ashokvarma.bottomnavigation.BottomNavigationBar for nav bar. here is the method for that purpose:

public void changeNavAndBarStats(String tag){
    if (tag == "flash"){
        bottomNavigationBar.selectTab(2,false);
        main_drawer.setSelection(flashcards.getIdentifier(),false);
    }else if (tag == "dic"){
        bottomNavigationBar.selectTab(3,false);
        main_drawer.setSelection(dictionary.getIdentifier(),false);
    }else if (tag == "Home"){
        bottomNavigationBar.selectTab(0,false);
        main_drawer.setSelection(home.getIdentifier(),false);
    }
}

So I call my fragments Like this:

MainFragmentChanger(new MainPageFragment(),"Home");
                            bottomNavigationBar.selectTab(0,false);

changeBottomFragment(new FlashCardFragment(),"flash");
                            bottomNavigationBar.selectTab(2,false);

changeBottomFragment(new TranslateFragment(),"dic");
                            bottomNavigationBar.selectTab(3,false);

At the end I call changeNavAndBarStatus in my fragment's onResume method:

((MainPageActivity)getContext()).changeNavAndBarStats("flash");

That's it! you are good to go!

Nigeria answered 24/11, 2018 at 8:1 Comment(0)
H
2

Try this to achieve the following: on back press: from home fragment exit the app. from other fragments goto home fragment.

    //On Back Press if we are at a Fragment other than the Home Fragment it will navigate back to the
// Home Fragment. From Home Fragment it will exit the App.
@Override
public void onBackPressed() {
    int backStackEntryCount = getSupportFragmentManager().getBackStackEntryCount();
    if (backStackEntryCount == 0) {
        super.onBackPressed();  
    } else {
        goHome();
    }
}

    public void goHome() {
    //Following code will set the icon of the bottom navigation to active
    final BottomNavigationView mBottomNav = findViewById(R.id.nav_view);
    MenuItem homeItem = mBottomNav.getMenu().getItem(0);
    mBottomNav.setSelectedItemId(homeItem.getItemId());
    getSupportFragmentManager().popBackStackImmediate();

    //To delete all entries from back stack immediately one by one.
    int backStackEntry = getSupportFragmentManager().getBackStackEntryCount();
    for (int i = 0; i < backStackEntry; i++) {
        getSupportFragmentManager().popBackStackImmediate();
    }
    //To navigate to the Home Fragment
    final HomeFragment homeFragment = new HomeFragment();
    FragmentTransaction myFragmentTransaction = getSupportFragmentManager().beginTransaction();
    myFragmentTransaction.replace(R.id.nav_host_fragment, homeFragment, "HomeFrag Tag");
    myFragmentTransaction.commit();
}
Heddie answered 13/5, 2020 at 11:3 Comment(0)
P
2

You can try this / its worked for me

public class MainActivity extends BaseActivity {
    private HomeFragment homeFragment = new HomeFragment();
    private CartFragment cartFragment = new CartFragment();
    private ProfileFragment profileFragment = new ProfileFragment();
    private BottomNavigationView bottomNavigationView;  



@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
  
    super.onCreate(savedInstanceState);
    if (savedInstanceState == null){
        setContentView(R.layout.activity_main);
        bottomNavigationView = findViewById(R.id.bottom_nav);
        bottomNavigationView.setSelectedItemId(R.id.btnHome);
        FragmentTransaction homeFm = getSupportFragmentManager().beginTransaction();
        homeFm.replace(R.id.fragment_container, homeFragment);
        homeFm.commit();
        setupView();
    }

}

private void setupView() {
    bottomNavigationView.setOnNavigationItemSelectedListener(item -> {
        {
            switch (item.getItemId()) {
                case R.id.btnHome:
                    loadFragment(homeFragment);
                    return true;
                case R.id.btnCart:
                    loadFragment(cartFragment);
                    return true;
                case R.id.btnProfile:
                    loadFragment(profileFragment);
                    return true;
            }
            return false;
        }
    });
}


private void loadFragment(Fragment fragment) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.fragment_container, fragment);
    transaction.addToBackStack(null);
    transaction.commit();
}
@Override
public void onBackPressed() {
     if (bottomNavigationView.getSelectedItemId() == R.id.btnHome)
    {
        finish();
    }
    else
    {
        bottomNavigationView.setSelectedItemId(R.id.btnHome);
    }
}}
Proficiency answered 5/1, 2021 at 9:41 Comment(0)
J
1

Use addToBackStack Method when calling a fragment like this,

   getSupportFragmentManager().beginTransaction().addToBackStack(null).add(R.id.content_home_nav,newFragment).commit();

Use this code in your onBackPressed Method

if (getSupportFragmentManager().getBackStackEntryCount() > 0 ){
        getFragmentManager().popBackStack();
    } else {
        super.onBackPressed();
    }
Jackquelinejackrabbit answered 10/5, 2017 at 7:36 Comment(0)
B
1

For your requirement you would be working with fragments on navigation for this you can use Tablayout with view pager and make bottom navigation.

<android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="60dp"></android.support.design.widget.TabLayout>

and then setup viewpager with tab layout and add icon to tablayout in your activity

tabLayout = (TabLayout) findViewById(R.id.tab_layout);
    viewPager = (ViewPager) findViewById(R.id.controller_pager);
    viewPager.setAdapter(new ViewPagerAdapter(getSupportFragmentManager()));
    viewPager.setOffscreenPageLimit(4);
    tabLayout.setupWithViewPager(viewPager);
    tabLayout.getTabAt(0).setIcon(R.drawable.selector_home);
    tabLayout.getTabAt(1).setIcon(R.drawable.selector_contact);
    tabLayout.getTabAt(2).setIcon(R.drawable.selector_profile);
    tabLayout.getTabAt(3).setIcon(R.drawable.selector_settings);

now handle all things on the click of tablayout and it will work fine tabLayout.addOnTabSelectedListener(this);

Bisset answered 10/5, 2017 at 7:48 Comment(0)
M
1

I was facing the same problem but after doing this I got the solution

First Paste this code in your main activity (where you are using Bottom navigation bar)

BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);

And then create a class named BottomNavigationViewHelper and paste the following code.

public class BottomNavigationViewHelper {
public static void disableShiftMode(BottomNavigationView view) {
    BottomNavigationMenuView menuView = (BottomNavigationMenuView) view.getChildAt(0);
    try {
        Field shiftingMode = menuView.getClass().getDeclaredField("mShiftingMode");
        shiftingMode.setAccessible(true);
        shiftingMode.setBoolean(menuView, false);
        shiftingMode.setAccessible(false);
        for (int i = 0; i < menuView.getChildCount(); i++) {
            BottomNavigationItemView item = (BottomNavigationItemView) menuView.getChildAt(i);
            //noinspection RestrictedApi
            item.setShiftingMode(false);
            // set once again checked value, so view will be updated
            //noinspection RestrictedApi
            item.setChecked(item.getItemData().isChecked());
        }
    } catch (NoSuchFieldException e) {
        Log.e("BNVHelper", "Unable to get shift mode field", e);
    } catch (IllegalAccessException e) {
        Log.e("BNVHelper", "Unable to change value of shift mode", e);
    }
}

}

Hope It helps

Manikin answered 8/7, 2017 at 11:16 Comment(0)
C
1

Try this.

getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
        @Override
        public void onBackStackChanged() {

            // Gets the previous global stack count
            int previousStackCount = mPreviousStackCount;

            // Gets a FragmentManager instance
            FragmentManager localFragmentManager = getSupportFragmentManager();

            // Sets the current back stack count
            int currentStackCount = localFragmentManager.getBackStackEntryCount();

            // Re-sets the global stack count to be the current count
            mPreviousStackCount = currentStackCount;

            boolean popping = currentStackCount < previousStackCount;

            if(popping){
                bottomNavigationView.getMenu().getItem(0).setChecked(true);
            }

        }
    });
Caespitose answered 3/10, 2017 at 10:21 Comment(0)
B
1

Please try this solution. I have made changes in your code given in question.

I have assumed that on back pressing first time your app will come back to home fragment (in your case Announcement fragment) and if you back press again, the app will close.

This flow will also reflect in bottom navigation bar.

public class MainActivity extends AppCompatActivity {
private static final String BACK_STACK_ROOT_TAG = "root_home_fragment";
private static final String SELECTED_ITEM = "selected_item";
private Fragment fragment;
private FragmentManager fragmentManager;
private BottomNavigationView bottomNavigationView;
private Toolbar toolbar;
private MenuItem menuItemSelected;
private int mMenuItemSelected;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
        = new BottomNavigationView.OnNavigationItemSelectedListener() {

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        boolean selected = false;
        switch (item.getItemId()) {
            case R.id.action_announcement:
                fragment = AnnouncementFragment.newInstance();
                selected =  true;
                break;
            case R.id.action_menu:
                fragment = MenuFragment.newInstance();
                selected =  true;
                break;
            case R.id.action_menu_reports:
                fragment = ReportFragment.newInstance();
                selected =  true;
                break;
        case R.id.action_setting:
        fragment = SettingFragment.newInstance();
                selected =  true;

        }
        if(fragment !=null){
            fragmentManager = getFragmentManager();
            switch (item.getItemId()) {
              case R.id.action_announcement:
                   // Pop every fragment from backstack including home fragment.
                   fragmentManager.popBackStack(BACK_STACK_ROOT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
                   fragmentManager.beginTransaction()
                            .replace(R.id.content, fragment)
                            .addToBackStack(BACK_STACK_ROOT_TAG)
                            .commit();
                    break;
               default: 

                   fragmentManager.beginTransaction()
                            .replace(R.id.content, fragment)
                            .addToBackStack(null)
                            .commit();  
                   break;
           }
        }
        return selected;
    }

};


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

    toolbar = (Toolbar) findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);

    bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation);
    bottomNavigationView.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);

    //Always load first fragment as default
    bottomNavigationView.setSelectedItemId(R.id.action_announcement);

    if (savedInstanceState != null) {
        mMenuItemSelected = savedInstanceState.getInt(SELECTED_ITEM, 0);
        menuItemSelected = bottomNavigationView.getMenu().findItem(mMenuItemSelected);
    } else {
        menuItemSelected = bottomNavigationView.getMenu().getItem(0);
    }
}



@Override
protected void onSaveInstanceState(Bundle outState) {
    outState.putInt(SELECTED_ITEM, mMenuItemSelected);
    super.onSaveInstanceState(outState);
}

public void onBackPressed() {
    int count = getFragmentManager().getBackStackEntryCount();
    if(count >1){
        // We have lots of fragment on backstack to be popped.
        // Pop till the root fragment.
        getFragmentManager().popBackStack(BACK_STACK_ROOT_TAG, FragmentManager.POP_BACK_STACK_INCLUSIVE);
     bottomNavigationView.setSelectedItemId(R.id.action_announcement);
    }
    else{
        // Close the application when we are on home fragment.
        supportFinishAfterTransition();
    }
}
}
Bertrand answered 4/11, 2017 at 21:12 Comment(0)
A
1

Most time when you, press back button, old fragment in the back stack, are recalled. therefore the system call this onCreateView() method

there add this code

val bottomNav = activity?.findViewById<BottomNavigationView>(R.id.activity_main_bottom_navigation)
    bottomNav?.selectedItemId = R.id.the_id_of_the_icon__that_represent_the_fragment
Avelinaaveline answered 6/8, 2018 at 15:53 Comment(0)
P
1

I did this after trying all and everything and at last it worked -_- .I have pasted this 2 override method in my each and every activity that i am surfing through my bottom navigation.

  @Override
protected void onResume() {
    super.onResume();
    bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation_Menu_name);
    bottomNavigationView.getMenu().getItem(Menu_item_position).setChecked(true);

}

@Override
protected void onRestart() {
    super.onRestart();
    bottomNavigationView = (BottomNavigationView) findViewById(R.id.bottom_navigation_Menu_name);
    bottomNavigationView.getMenu().getItem(Menu_item_position).setChecked(true);
}
Providential answered 20/11, 2018 at 7:41 Comment(0)
E
1

Late answer but here it goes.

Let's say you have a BottomNavigation inside MainActivity with 4 Fragments.

  1. FragmentA
  2. FragmentB
  3. FragmentC
  4. FragmentD

If your are adding each fragment to backstack like so:

With kotlin:

 main_bottom_navigation.setOnNavigationItemSelectedListener { item ->
        var fragment: Fragment? = null
        when (item.itemId) {
            R.id.nav_a -> fragment = FragmentA()
            R.id.nav_b -> fragment = FragmentB()
            R.id.nav_c -> fragment = FragmentC()
            R.id.nav_d -> fragment = FragmentD()
        }

        supportFragmentManager
            .beginTransaction()
            .setCustomAnimations(R.anim.abc_fade_in, R.anim.abc_fade_out)
            .replace(R.id.home_content, fragment!!)
            .addToBackStack(fragment.tag)
            .commit()

        true
    }

You dont really need to override onBackPressed() inside the MainActivity but explicitly cast on each fragment and assigning the BottomNavigation like this:

FragmentA.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(0).isChecked = true
}

FragmentB.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(1).isChecked = true
}

FragmentC.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(2).isChecked = true
}

FragmentD.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    (activity as MainActivity).main_bottom_navigation.menu.getItem(3).isChecked = true
}

Like this the fragment backstack will properly pop and even exit application when it reaches 0.

Excommunication answered 21/2, 2019 at 2:43 Comment(2)
Fragments should not know about internal details of activities that may or may not host them.Anecdotage
This is brilliant.Unaccompanied
H
1

This is what I did to handle back press in the activity:

    @Override
public void onBackPressed() {
    super.onBackPressed();

    if(homeFragment.isVisible()){
        navView.setSelectedItemId(R.id.nav_home);
    }
    if(searchFragment.isVisible()){
        navView.setSelectedItemId(R.id.nav_search);
    }
    if(myProfileFragment.isVisible()){
        navView.setSelectedItemId(R.id.nav_profile);
    }

}
Hatcher answered 17/9, 2019 at 8:20 Comment(0)
P
1

this code is work for me:

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

    OneSignal.startInit(this)
            .inFocusDisplaying(OneSignal.OSInFocusDisplayOption.Notification)
            .unsubscribeWhenNotificationsAreDisabled(true)
            .init();

    bottomNavigationView = findViewById(R.id.bottomNav);
    frameLayout = findViewById(R.id.main_frame);

    homeFragment = new HomeFragment();
    aboutUsFragment = new AboutUsFragment();
    recipesFragment = new RecipesFragment();
    knowledgeFragment = new KnowledgeFragment();
    contactFragment = new ContactFragment();

    loadFragment(homeFragment);

    bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
        @Override
        public boolean onNavigationItemSelected(@NonNull MenuItem menuItem) {

            switch (menuItem.getItemId()) {

                case R.id.home:
                    //mManiNav.setItemBackgroundResource(R.color.blue);
                    loadFragment(homeFragment);
                    return true;

                case R.id.deposit:
                    // mManiNav.setItemBackgroundResource(R.color.colorAccent);
                    loadFragment(recipesFragment);
                    return true;

                case R.id.exchange:
                    //mManiNav.setItemBackgroundResource(R.color.colorPrimary);
                    loadFragment(knowledgeFragment);
                    return true;
                case R.id.profile:
                    // mManiNav.setItemBackgroundResource(R.color.light_blue);
                    loadFragment(aboutUsFragment);
                    return true;

                case R.id.payout:
                    // mManiNav.setItemBackgroundResource(R.color.light_blue);
                    loadFragment(contactFragment);
                    return true;



                default:
                    return false;

            }
        }
    });

}

Here the load fragment class:

 private void loadFragment(Fragment fragment) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.replace(R.id.main_frame, fragment);
    transaction.addToBackStack(null);
    transaction.commit();
}

Here is the popBackStackTillEntry method:

enter code here  public void popBackStackTillEntry(int entryIndex) {

    if (getSupportFragmentManager() == null) {
        return;
    }
    if (getSupportFragmentManager().getBackStackEntryCount() <= entryIndex) {
        return;
    }
    FragmentManager.BackStackEntry entry = getSupportFragmentManager().getBackStackEntryAt(
            entryIndex);
    if (entry != null) {
        getSupportFragmentManager().popBackStackImmediate(entry.getId(),
                FragmentManager.POP_BACK_STACK_INCLUSIVE);
    }


}

Here is the backpress method:

boolean doubleBackToExitPressedOnce = false;
@Override
public void onBackPressed() {
    if (doubleBackToExitPressedOnce) {
        popBackStackTillEntry(0);
        moveTaskToBack(true);
        System.exit(0);
        return;
    }

    this.doubleBackToExitPressedOnce = true;
    loadFragment(new HomeFragment());

    new Handler().postDelayed(new Runnable() {

        @Override
        public void run() {
            doubleBackToExitPressedOnce=false;
        }
    }, 2000);
}
Pauli answered 5/3, 2021 at 10:44 Comment(0)
R
1

This is a simple and complete working code in Kotlin.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

            bottomNavigationView = findViewById(R.id.bottom_navigation)
            bottomNavigationView.setOnItemSelectedListener {menuItem ->
            when(menuItem.itemId){
                R.id.navBottom_menu_1 ->    nextFragment(Fragment_1())
                R.id.navBottom_menu_2 ->    nextFragment(Fragment_2())
                R.id.navBottom_menu_3 ->    nextFragment(Fragment_3())
                R.id.navBottom_menu_4 ->    nextFragment(Fragment_4())
                else ->false
            }
        }
    }
    
    fun nextFragment(fm:Fragment): Boolean {
        supportFragmentManager.beginTransaction().replace(R.id.linearLayout_rootFragment, fm).commit()
        return true
    }

    
    fun isMenuChecked(itemIndex:Int):Boolean{
        return bottomNavigationView.menu.getItem(itemIndex).isChecked
    }

    
    fun setMenuItemChecked(itemIndex:Int){
        bottomNavigationView.menu.getItem(itemIndex).isChecked = true
    }

    
    override fun onBackPressed() {
        when(true){
            isMenuChecked(3) -> {nextFragment(Fragment_3()) ; setMenuItemChecked(2) }
            isMenuChecked(2) -> {nextFragment(Fragment_2()) ; setMenuItemChecked(1) }
            isMenuChecked(1) -> {nextFragment(Fragment_1()) ; setMenuItemChecked(0) }
            else -> super.onBackPressed()
        }
    }
    
}
Rayshell answered 14/11, 2021 at 16:0 Comment(0)
S
1

This below code will give the ability to navigate to the first position (top-level destination) when the back button is pressed.

    val navController = findNavController(R.id.nav_host_fragment)
    val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
    
    // Step 2: Set up NavController with BottomNavigationView
    bottomNavigationView.setupWithNavController(navController)
    
    // Step 3: Create OnBackPressedCallback
    val onBackPressedCallback = object : OnBackPressedCallback(true) {
        override fun handleOnBackPressed() {
            // Step 5: Check if not at top-level destination and navigate to top-level destination
            if (!navController.popBackStack(R.id.top_level_destination, false)) {
                // If already at top-level destination, handle back button press as normal
                isEnabled = false
                requireActivity().onBackPressedDispatcher.onBackPressed()
            }
        }
    }
    
    // Step 4: Set OnBackPressedCallback for NavController
    navController.addOnDestinationChangedListener { _, destination, _ ->
        onBackPressedCallback.isEnabled = destination.id != R.id.top_level_destination
    }
    
    requireActivity().onBackPressedDispatcher.addCallback(this, onBackPressedCallback)
Saunder answered 15/5, 2023 at 15:25 Comment(0)
K
0

If it's desirable for you that the home tab preserves the stack state when returning from another tab using the back button (in the same way tabs will preserve stack state when switching between them), then you might find this useful. Also using the new back pressed dispatcher API rather than the deprecated onBackPressed. (from onCreate)

  private fun setupBackPressedHandling2() {
    onBackPressedDispatcher.addCallback(this) {
        val backStackCount = navHostFragment?.childFragmentManager?.backStackEntryCount ?: 0
        if (currentTab != HOME && backStackCount <= 1) {
            selectTab(HOME)
        } else {
            invokeDefaultBackPressedHandling(navController)
        }
    }
}

where...

fun AppCompatActivity.invokeDefaultBackPressedHandling(navController: NavController?) {
when (navController) {
    null -> {
        if (!supportFragmentManager.isStateSaved && !supportFragmentManager.popBackStackImmediate()) {
            finish()
        }
    }
    else -> {
        if (!navController.popBackStack()) {
            finish()
        }
    }
}}
Khachaturian answered 2/2 at 9:46 Comment(0)
C
-1

This is how I solved my,

  1. Wrap your main widget in WillPopScope() and set a function in the onWillpop: as this
Future<bool> _onBackpress() {
    if (_currentpage != 0) {
      setState(() {
        _currentpage--;//decreases number of pages till the fisrt page
      });
    } else {
    // a function to close the app
    }
  }
Checkered answered 10/10, 2020 at 16:3 Comment(3)
this is not flutter question and Android Question.Dinin
I was searching for an answer like this in flutter so I decide to add it for anyone who might need it in flutterCheckered
create a seprate question of your answer them and accept it.With flutter tag.Dinin

© 2022 - 2024 — McMap. All rights reserved.