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

Dr.jacky
  • 3,341
  • 6
  • 53
  • 91
Ankit Tale
  • 1,924
  • 4
  • 17
  • 30
  • What exactly isn't working? Please describe the behavior you are *trying* to get and the unexpected behavior you *actually* getting. – Bryan May 09 '17 at 15:03
  • 1
    On BackPressed is not working properly and when I hit setting icon there is enough space between icon – Ankit Tale May 11 '17 at 04:51
  • You don't need to use any customization for bottom navigation bar. – MSS Nov 04 '17 at 20:49

22 Answers22

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);
            }
        }
    });
}
marco
  • 3,193
  • 4
  • 28
  • 51
  • 1
    does not work. it never calls the onBackStackChanged method, despite the fragmentTransaction.addtoBackstack(name) is called... – UDE_Student Sep 09 '17 at 12:13
  • 1
    adding a onBackStackChange listener every time is a bad practice. – Alan Deep May 15 '20 at 04:10
  • Not working. Call every time addOnBackStackChangedListener and count is every time 0 – emreturka Oct 19 '20 at 07:11
  • Hi @emreturka, this is a 3yo answer, today please refer to https://developer.android.com/guide/navigation. This solution worked fine at that time, now i haven't tried it anymore – marco Oct 19 '20 at 08:50
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);
}
Shailesh Bandil
  • 497
  • 7
  • 20
  • Every time set home fragment. – emreturka Oct 19 '20 at 06:19
  • 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. – Charly Lafon Nov 18 '21 at 16:58
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);
        }
    }
Vasu
  • 886
  • 2
  • 11
  • 28
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 :)

Fahd Al-Qahtani
  • 188
  • 1
  • 9
8

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

 @Override
 protected void onResume() {
    super.onResume();
    bottomNavigationView.getMenu().getItem(0).setChecked(true);
 }
Thriveni
  • 742
  • 9
  • 11
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);
    }
}
Bharata
  • 13,509
  • 6
  • 36
  • 50
ali hoseinpoor
  • 131
  • 1
  • 7
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!

MoeinDeveloper
  • 251
  • 1
  • 2
  • 11
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();
}
Saqib
  • 377
  • 4
  • 7
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);
    }
}}
Amir
  • 284
  • 1
  • 3
  • 7
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();
    }
scienticious
  • 438
  • 1
  • 7
  • 22
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);

pankaj yadav
  • 424
  • 4
  • 10
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

Rajat Mittal
  • 413
  • 5
  • 19
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);
            }

        }
    });
bagya gunawardana
  • 288
  • 1
  • 2
  • 8
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();
    }
}
}
MSS
  • 3,306
  • 1
  • 19
  • 50
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
Declan Nnadozie
  • 1,805
  • 1
  • 10
  • 20
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);
}
Ananna Rahman
  • 165
  • 1
  • 10
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.

Ispam
  • 495
  • 7
  • 18
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);
    }

}
fullmoon
  • 8,030
  • 5
  • 43
  • 58
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);
}
mahedi hassan
  • 141
  • 1
  • 4
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()
        }
    }
    
}
Satish Singh
  • 71
  • 1
  • 2
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)
Jay Patel
  • 89
  • 7
-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
    }
  }
Dharman
  • 30,962
  • 25
  • 85
  • 135
CodedRomero
  • 1
  • 1
  • 1