Fragment re-created on bottom navigation view item selected - android
Following is my code for bottom navigation view item selected
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.action_one:
// Switch to page one
fragment = FragmentA.newInstance();
break;
case R.id.action_two:
// Switch to page two
fragment = FragmentB.newInstance();
break;
case R.id.action_three:
// Switch to page three
fragment = FragmentC.newInstance();
break;
}
getSupportFragmentManager().beginTransaction().replace(R.id.container,fragment,"TAG").commit();
return true;
}
});
Now my problem is every time fragment is re-created and don't want fragment to be recreated every time I also tried adding addToBackStack(null) but it this case on back button press keeps popping fragments from stack which I don't want.
Is there any way to display fragments on bottom navigation bar selected without re-creating fragment
With support library v26 you can do this
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
Fragment curFrag = mFragmentManager.getPrimaryNavigationFragment();
if (curFrag != null) {
fragmentTransaction.detach(curFrag);
}
Fragment fragment = mFragmentManager.findFragmentByTag(tag);
if (fragment == null) {
fragment = new YourFragment();
fragmentTransaction.add(container.getId(), fragment, tag);
} else {
fragmentTransaction.attach(fragment);
}
fragmentTransaction.setPrimaryNavigationFragment(fragment);
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.commitNowAllowingStateLoss();
I faced the same problem and finally i i found the solution, you can try this code. it's work for me.
import android.support.annotation.NonNull;
import android.support.design.widget.BottomNavigationView;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.MenuItem;
import android.widget.FrameLayout;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
public BottomNavigationView bv;
public home_fragment home;
public account_fragment afrag;
public other_fragment other;
public FrameLayout fr;
android.support.v4.app.Fragment current;
//public FragmentTransaction frt;
public static int temp=0;
final Fragment fragment11 = new account_fragment();
final Fragment fragment22 = new home_fragment();
final Fragment fragment33 = new other_fragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment11;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
bv=(BottomNavigationView) findViewById(R.id.navigationView);
fm.beginTransaction().add(R.id.main_frame, fragment33, "3").hide(fragment33).commit();
fm.beginTransaction().add(R.id.main_frame, fragment22, "2").hide(fragment22).commit();
fm.beginTransaction().add(R.id.main_frame,fragment11, "1").commit();
bv.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.account:
fm.beginTransaction().hide(active).show(fragment11).commit();
active = fragment11;
return true;
case R.id.home1:
fm.beginTransaction().hide(active).show(fragment22).commit();
active = fragment22;
return true;
case R.id.other:
fm.beginTransaction().hide(active).show(fragment33).commit();
active = fragment33;
return true;
}
return false;
}
});
bv.setOnNavigationItemReselectedListener(new BottomNavigationView.OnNavigationItemReselectedListener() {
#Override
public void onNavigationItemReselected(#NonNull MenuItem item) {
Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();
}
});
}
}
This seemed to work well for me. Instead of attaching and detaching, i use show or hide to maintain fragment state.
public void changeFragment(Fragment fragment, String tagFragmentName) {
FragmentManager mFragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment();
if (currentFragment != null) {
fragmentTransaction.hide(currentFragment);
}
Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName);
if (fragmentTemp == null) {
fragmentTemp = fragment;
fragmentTransaction.add(R.id.frame_layout, fragmentTemp, tagFragmentName);
} else {
fragmentTransaction.show(fragmentTemp);
}
fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp);
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.commitNowAllowingStateLoss();
}
And this is how i use it
private void initViews() {
BottomNavigationView bottomNavigationView = findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener
(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment selectedFragment = null;
switch (item.getItemId()) {
case R.id.explore:
changeFragment(new ExploreFragment(), ExploreFragment.class
.getSimpleName());
toggleViews(true, "");
break;
case R.id.favorite:
changeFragment(new FavoriteFragment(), FavoriteFragment.class
.getSimpleName());
toggleViews(false, "Favorites");
break;
case R.id.venue:
changeFragment(new VenueFragment(), VenueFragment.class.getSimpleName());
toggleViews(false, "Venues");
break;
case R.id.profile:
changeFragment(new ProfileFragment(), ProfileFragment.class
.getSimpleName());
toggleViews(false, "Profile");
break;
}
return true;
}
});
//Manually displaying the first fragment - one time only
changeFragment(new ExploreFragment(), ExploreFragment.class
.getSimpleName());
}
This solution stops a fragment from being re-created when the currently selected nav button is clicked again. However, a new fragment will be created every time the user clicks a different nav button.
Just add this line to avoid re-created Fragment from BottomNavigationView
bottomNavigation.setOnNavigationItemReselectedListener(new BottomNavigationView.OnNavigationItemReselectedListener() {
#Override
public void onNavigationItemReselected(#NonNull MenuItem item) {
// do nothing here
}
});
Or with a lambda:
bottomNavigation.setOnNavigationItemReselectedListener(item -> { });
Be careful when using replace. Even if providing a fragment that already exists in memory, replace will restart the fragment's lifecycle. To avoid a restart, the transaction object's methods includes add, show, and hide, which can be used to show the correct fragment without restarting it.
private fun switchFragment(selectedTabIndex: Int) {
val previousTabIndex = this.currentTabIndex
this.currentTabIndex = selectedTabIndex
val transaction = supportFragmentManager.beginTransaction()
val tag = fragments[this.currentTabIndex].tag
// if the fragment has not yet been added to the container, add it first
if (supportFragmentManager.findFragmentByTag(tag) == null) {
transaction.add(R.id.container, fragments[this.currentTabIndex], tag)
}
transaction.hide(fragments[previousTabIndex])
transaction.show(fragments[this.currentTabIndex])
transaction.commit()
}
Try this :
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.container);
switch (item.getItemId()) {
case R.id.action_one:
// Switch to page one
if (!(currentFragment instanceof FragmentA)) {
fragment = FragmentA.newInstance();
}
break;
case R.id.action_two:
// Switch to page two
if (!(currentFragment instanceof FragmentB)) {
fragment = FragmentB.newInstance();
}
break;
case R.id.action_three:
// Switch to page three
if (!(currentFragment instanceof FragmentC)) {
fragment = FragmentC.newInstance();
}
break;
}
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment, "TAG").commit();
return true;
}
});
That will get the current fragment in your container and if you click again on this fragment that will not re add the fragment.
setOnNavigationItemReselectedListener would be a better solution for that
Use setOnNavigationItemReselectedListener like this:
private BottomNavigationView.OnNavigationItemReselectedListener onNavigationItemReselectedListener
= new BottomNavigationView.OnNavigationItemReselectedListener() {
#Override
public void onNavigationItemReselected(#NonNull MenuItem item) {
Toast.makeText(MainActivity.this, "Reselected", Toast.LENGTH_SHORT).show();
}
};
and call it using:
navigation.setOnNavigationItemReselectedListener(onNavigationItemReselectedListener);
The best way i found to do it.
private void replace_fragment(Fragment fragment) {
String tag = fragment.getClass().getSimpleName();
FragmentTransaction tr = getSupportFragmentManager().beginTransaction();
Fragment curFrag = getSupportFragmentManager().getPrimaryNavigationFragment();
Fragment cacheFrag = getSupportFragmentManager().findFragmentByTag(tag);
if (curFrag != null)
tr.hide(curFrag);
if (cacheFrag == null) {
tr.add(R.id.main_frame, fragment, tag);
} else {
tr.show(cacheFrag);
fragment = cacheFrag;
}
tr.setPrimaryNavigationFragment(fragment);
tr.commit();
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.nav_posts:
replace_fragment(new PostsFragment());
return true;
case R.id.nav_stores:
replace_fragment(new StoresFragment());
return true;
case R.id.nav_chats:
replace_fragment(new DiscussionsFragment());
return true;
case R.id.nav_account:
replace_fragment(new ProfileFragment());
return true;
}
return false;
}
};
I've improved #Viven's answers and written it with Kotlin. My version instantiates fragment only for the first time, hides/shows. I'm new at Kotlin so tell me if I can improve anything.
We need to hold a id to tag map:
private val fragmentTags = hashMapOf(
R.id.action_home to "home_fragment",
R.id.action_profile to "profile_fragment"
)
The listener code:
bottomNavigation.run {
setOnNavigationItemSelectedListener { menuItem ->
supportFragmentManager.beginTransaction()
.let { transaction ->
// hide current fragment
supportFragmentManager.primaryNavigationFragment?.let {
// if selected fragment's tag is same, do nothing.
if (it.tag == fragmentTags[menuItem.itemId]) {
return#setOnNavigationItemSelectedListener true
}
transaction.hide(it)
}
var fragment = supportFragmentManager.findFragmentByTag(fragmentTags[menuItem.itemId])
if (fragment == null) {
// instantiate fragment for the first time
fragment = when(menuItem.itemId) {
R.id.action_home -> HomeFragment()
R.id.action_profile -> ProfileFragment()
else -> null
}?.also {
// and add it
transaction.add(R.id.frame, it, fragmentTags[menuItem.itemId])
}
} else {
// if it's found show it
transaction.show(fragment)
}
transaction
.setPrimaryNavigationFragment(fragment)
.setReorderingAllowed(true)
}.commitNowAllowingStateLoss()
return#setOnNavigationItemSelectedListener true
}
//bottomNavigation things
itemIconTintList = null
selectedItemId = R.id.action_home
}
I solved this problem by adding a ViewPager to which I delegated all my navigation fragments. Its adapter (FragmentPagerAdapter) doesn't recreate the fragments instances when the user navigates through the BotoomNavigationView.
To achieve this, you have to complete 5 easy steps:
add a ViewPager to your layout;
implement its adapter:
class YourNavigationViewPagerAdapter(fm: FragmentManager,
private val param1: Int,
private val param2: Int)
: FragmentPagerAdapter(fm) {
override fun getItem(p0: Int) = when(p0) {
0 -> NavigationFragment1.newInstance(param1, param2)
1 -> NavigationFragment2.newInstance(param1, param2)
2 -> NavigationFragment3.newInstance(param1, param2)
else -> null
}
override fun getCount() = 3
}
don't forget to set the new adapter:
yourViewPager.adapter = YourNavigationViewPagerAdapter(supportFragmentManager, param1, param2)
set a OnNavigationItemSelectedListener to your BottomNavigationView like the following:
yourBottomNavigationView.setOnNavigationItemSelectedListener {
when(it.itemId) {
R.id.yourFirstFragmentMenuItem -> {
yourViewPager.currentItem = 0
true
}
R.id.yourSecondFragmentMenuItem -> {
yourViewPager.currentItem = 1
true
}
R.id.yourThirdFragmentMenuItem -> {
yourViewPager.currentItem = 2
true
}
else -> false
}
}
set a OnPageChangeListener to your ViewPager like the following:
yourViewPager.addOnPageChangeListener(object :
ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(p0: Int) {
}
override fun onPageScrolled(p0: Int, p1: Float, p2: Int) {
}
override fun onPageSelected(p0: Int) {
yourBottomNavigationView.menu.getItem(p0).isChecked = true
}
})
enjoy :)
I wrote an Extension function in Kotlin for the same.
fun FragmentManager.switch(containerId: Int, newFrag: Fragment, tag: String) {
var current = findFragmentByTag(tag)
beginTransaction()
.apply {
//Hide the current fragment
primaryNavigationFragment?.let { hide(it) }
//Check if current fragment exists in fragmentManager
if (current == null) {
current = newFrag
add(containerId, current!!, tag)
} else {
show(current!!)
}
}
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.setPrimaryNavigationFragment(current)
.setReorderingAllowed(true)
.commitNowAllowingStateLoss()
}
There are several test cases involved in proper navigation , I am pasting my code with all test cases checked.
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
switch (item.getItemId()) {
case R.id.dashboard:
Fragment fragment;
fragment = fragmentManager.findFragmentByTag(DashboardFragment.TAG);
if (fragment == null) {
fragment = new DashboardFragment();
fragmentTransaction.add(R.id.frame, fragment, DashboardFragment.TAG);
} else {
fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
fragmentTransaction.attach(fragment);
}
fragmentTransaction.setPrimaryNavigationFragment(fragment);
fragmentTransaction.commitNow();
return true;
case R.id.expenses:
fragment = fragmentManager.findFragmentByTag(ExpenseFragment.TAG);
if (fragment == null) {
fragment = new ExpenseFragment();
fragmentTransaction.add(R.id.frame, fragment, ExpenseFragment.TAG);
} else {
fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
fragmentTransaction.attach(fragment);
}
fragmentTransaction.setPrimaryNavigationFragment(fragment);
fragmentTransaction.commitNow();
return true;
case R.id.vehicle_parts:
Bundle bundle = new Bundle();
bundle.putInt("odometer", vehicle.getOdometer());
bundle.putString("vehicle_id", vehicle.get_id());
fragment = fragmentManager.findFragmentByTag(PartsFragment.TAG);
if (fragment == null) {
fragment = new PartsFragment();
fragment.setArguments(bundle);
fragmentTransaction.add(R.id.frame, fragment, PartsFragment.TAG);
} else {
fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
fragmentTransaction.attach(fragment);
}
fragmentTransaction.setPrimaryNavigationFragment(fragment);
fragmentTransaction.commitNow();
return true;
case R.id.blog:
fragment = fragmentManager.findFragmentByTag(BlogFragment.TAG);
if (fragment == null) {
fragment = new BlogFragment();
fragmentTransaction.add(R.id.frame, fragment, BlogFragment.TAG);
} else {
fragmentTransaction.detach(fragmentManager.getPrimaryNavigationFragment());
fragmentTransaction.attach(fragment);
}
fragmentTransaction.setPrimaryNavigationFragment(fragment);
fragmentTransaction.commitNow();
return true;
}
return false;
Use Cicerone library for handling navigation easily.
https://github.com/terrakok/Cicerone
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.save: {
router.replaceScreen("your fragment1");
menuItem.setChecked(true);
break;
}
case R.id.search_lessons: {
router.replaceScreen("your fragment2");
menuItem.setChecked(true);
break;
}
case R.id.profile_student: {
router.replaceScreen("your fragment3");
menuItem.setChecked(true);
break;
}
}
return false;
}
How To stop recreating Fragment when it is Already Visible Using BottomNavigationView
Step 1--
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
String valuestring;
**if (item.getItemId() == lastSelectedItemId) { // added this
Log.e( "both id is same","LULL" );
return true;
}**
switch (item.getItemId()) {
case R.id.navigation_category:
SetActioBarText( getString( R.string.label_actionbar_categories ) );
fragment = new CategoryFragment();
valuestring = "category";
break;
case R.id.navigation_favourite:
SetActioBarText( getString( R.string.label_actionbar_favourites ) );
fragment = new FavouriteFragment();
valuestring = "favourites";
break;
default:
throw new IllegalStateException( "Unexpected value: " + menuItem.getItemId() );
}
return loadFragment( fragment, valuestring ,menuItem);
}
Now Step 2---
private boolean loadFragment(Fragment fragment, String argument,MenuItem item) {
if (fragment != null) {
transaction = fragmentManager.beginTransaction();
transaction.addToBackStack( argument );
transaction.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_FADE );
transaction.replace( R.id.frame_container, fragment, "demofragment").commitAllowingStateLoss();
lastSelectedItemId= item.getItemId();
return true;
}
return false;
}
no need to do this answers. actually what you really need is to save view in specific fragmet.
private View view;
if(view == null){
view = inflater.inflate(R.layout.call_fragment_edited, container, false);
}
so any time when you create new fragment you see current state
Related
How to add an activity in bottom navigation view that already contains fragments
I'm using 4 fragments and the MainActivity (which contains the bottom nav) but now because of a particular payment gateway issue with one of the fragments I have changed that fragment into an activity, but as the bottom nav has 3 fragments in it don't know how do add an activity in it, and I also don't want to change all the 3 remaining fragments into activity just to make it work with bottom nav Files MainActivity,Home_Fragment,Caution_Fragment,Donate_Fragment // this is the fragment i changed into activtiy// and About_Fragment is there any way to add an activity in a bottom navigation view that already contains fragments if you want to view the Donate_Fragment(Activity) code please tell me I will add it MainActivity.java public class MainActivity extends AppCompatActivity { public BottomNavigationView bottomNavigationView; Deque<Integer> integerDeque = new ArrayDeque<>(3); boolean flag = true; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Window window = this.getWindow(); window.setStatusBarColor(this.getResources().getColor(R.color.black)); bottomNavigationView = findViewById(R.id.bottomNavigationView); bottomNavigationView.setItemIconTintList(null); integerDeque.push(R.id.home_icon); loadFragments(new Home_Fragment()); bottomNavigationView.setSelectedItemId(R.id.home_icon); bottomNavigationView.setOnNavigationItemSelectedListener( item -> { int id = item.getItemId(); if (integerDeque.contains(id)) { if (id == R.id.home_icon) { integerDeque.size(); if (flag) { integerDeque.addFirst(R.id.home_icon); flag = false; } } integerDeque.remove(id); } integerDeque.push(id); loadFragments(getFragment(item.getItemId())); return true; } ); } #SuppressLint("NonConstantResourceId") private Fragment getFragment(int itemId) { switch (itemId) { case R.id.home_icon: bottomNavigationView.getMenu().getItem(0).setChecked(true); return new Home_Fragment(); case R.id.guide_icon: bottomNavigationView.getMenu().getItem(1).setChecked(true); return new Cuation_Fragment(); case R.id.donate_icon: bottomNavigationView.getMenu().getItem(2).setChecked(true); return new Donate_Fragment(); case R.id.about_icon: bottomNavigationView.getMenu().getItem(3).setChecked(true); return new About_Fragment(); } bottomNavigationView.getMenu().getItem(0).setChecked(true); return new Home_Fragment(); } public void loadFragments(Fragment fragment) { if (fragment != null) { getSupportFragmentManager().beginTransaction() .replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName()) .commit(); } } #Override public void onBackPressed() { integerDeque.pop(); if (!integerDeque.isEmpty()) { bottomNavigationView.setSelectedItemId(integerDeque.peek()); } else { finish(); } } }
Call API only once with Bottom Navigatioin Fragments
I am having a bottom navigation fragment and want to call API only once, which means when the user jumps to another tab and comes back API should not be getting called. And when the user comebacks from the background state, API should be called once. I tried calling API with the onCreate method, but oncreate method calling every time when I click on the tab. On the other side, for one fragment I am sending data from MainActivity by calling the API. Please check the code and shed some light on where am doing wrong. studentBottomNavView.setOnNavigationItemSelectedListener (new BottomNavigationView.OnNavigationItemSelectedListener() { #Override public boolean onNavigationItemSelected(#NonNull MenuItem item) { Fragment selectedFragment = null; Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.stdcontainer); switch (item.getItemId()) { case R.id.navigation_std_current_course: if (!(currentFragment instanceof StdCurrentCourse)) { selectedFragment = StdCurrentCourse.newInstance(); } isHome = false; break; case R.id.navigation_std_points: if (currentFragment == null) { if (!(currentFragment instanceof StdPoints)) { selectedFragment = StdPoints.newInstance(); } } isHome = false; break; case R.id.navigation_std_home: if (!(currentFragment instanceof StdHome)) { selectedFragment = new StdHome(attendanceList, studentAbsenceAndDelays, todayTaskList); } isHome = true; break; case R.id.navigation_std_task_hw: if (!(currentFragment instanceof StdTaskAndHw)) { selectedFragment = StdTaskAndHw.newInstance(); } isHome = false; break; case R.id.navigation_chat: if (!(currentFragment instanceof StdRealTimeChat)) { selectedFragment = StdRealTimeChat.newInstance(); } isHome = false; break; } FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.replace(R.id.frame_layout1, selectedFragment); transaction.commit(); return true; } });
You can control API Calls using a Boolean, set as false once you have received a successful response and when BaseActivity/Fragent where studentBottomNavView is called onPause set the same Boolean as true as per your requirement.
Bottom Navigation & Fragment Recreation
I have a bottom navigation which runs on my Main Activity private void BottomNavigation_NavigationItemSelected(object sender, BottomNavigationView.NavigationItemSelectedEventArgs e) { LoadFragment(e.Item.ItemId); } void LoadFragment(int id) { Android.Support.V4.App.Fragment fragment = null; switch (id) { case Resource.Id.navigation_1: fragment = Fragment1.NewInstance(); break; case Resource.Id.navigation_2: fragment = Fragment2.NewInstance(); break; case Resource.Id.navigation_3: fragment = Fragment3.NewInstance(); break; } if (fragment == null) return; SupportFragmentManager.BeginTransaction() .Replace(Resource.Id.content_frame, fragment) .Commit(); } In my Fragment1 (which is Resource.Id.navigation_1) I am refreshing data public async override void OnCreate(Bundle savedInstanceState) { base.OnCreate(savedInstanceState); RefreshItems(false); } The issue is, every time I click on navigation_1 for fragment1, the data gets refreshed. I don't want to load the fragment/data again once it's already loaded. Is it possible in Xamarin?
You can check if current fragment also executed Here is a function public Fragment find(String identifier) { Fragment fragment = (Fragment) getSupportFragmentManager().findFragmentByTag(identifier); return fragment; } and in BottomNavigationView you must check like this if (find(fragment.getIdentifier()) != null) { return; }
I'd this problem sometime ago, however I was developing on native kotlin code, here goes the code I've used to handle this: class Navigation(val supportFragmentManager: FragmentManager, val filterIcon: ImageView ): BottomNavigationView.OnNavigationItemSelectedListener { override fun onNavigationItemSelected(item: MenuItem): Boolean { when(item.itemId){ R.id.menu_bottom_home ->{ changeFragment(HomeFragment(), Constants.FRAG_HOME_FRAGMENT) filterIcon.visibility = View.VISIBLE } R.id.menu_bottom_favorites ->{ changeFragment(FavoritesFragment(), Constants.FRAG_FAVORITES_FRAGMENT) filterIcon.visibility = View.INVISIBLE } R.id.menu_bottom_reservations ->{ changeFragment(ReservationFragment(), Constants.FRAG_RESERVATION_FRAGMENT) filterIcon.visibility = View.INVISIBLE } R.id.menu_bottom_profile ->{ changeFragment(ProfileFragment(), Constants.FRAG_PROFILE_FRAGMENT) filterIcon.visibility = View.INVISIBLE } } return true } fun changeFragment(fragment: Fragment, tag : String){ var contains: Fragment? = null var current: Fragment? = null if(supportFragmentManager.fragments != null && supportFragmentManager.fragments.isNotEmpty()) { contains = supportFragmentManager.fragments.firstOrNull { x -> x.tag == tag } current = supportFragmentManager.fragments.first { x -> x.isVisible } } if(current != null){ supportFragmentManager.beginTransaction() .hide(current).commit() } if(contains != null){ supportFragmentManager.beginTransaction() .show(contains).commit() } else{ supportFragmentManager.beginTransaction() .add(R.id.fl_fragment_container, fragment, tag) .addToBackStack(tag) .commit() } } } Then you just have to set setOnNavigationItemSelectedListener with your Navigation class like this: bnv_navigation.setOnNavigationItemSelectedListener(Navigation(supportFragmentManager, iv_filter_bar))
How to implement proper back navigation for the Android BottomNavigation
I spent my whole day now looking for an answer and didn't found any solution that suited this question. I am looking for a way to create a BottomNavigation usage similar to the one of the Instagram or PlayKiosk app. Fragments should be added to the back stack only once. When pressing the back button I'd expect the app to jump back to the last Fragment that was visited and the button of the BottomNavigation to fit that Fragment. Currently I use the following code: //BottomNavigationListener private BottomNavigationView.OnNavigationItemSelectedListener buttonNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { #Override public boolean onNavigationItemSelected(#NonNull MenuItem item) { FragmentManager fragmentManager = getSupportFragmentManager(); switch (item.getItemId()) { case R.id.navigation_game: currentFragment = GameFragment.newInstance(); fragmentManager.beginTransaction() .addToBackStack(TAG_FRAGMENT_GAME) .replace(R.id.content_frame, currentFragment, TAG_FRAGMENT_GAME) .commit(); break; case R.id.navigation_tournament: currentFragment = TournamentFragment.newInstance(); fragmentManager.beginTransaction() .addToBackStack(TAG_FRAGMENT_TOURNAMENT) .replace(R.id.content_frame, currentFragment, TAG_FRAGMENT_TOURNAMENT) .commit(); break; case R.id.navigation_history: break; } return true; } }; But this leads to the problem that I could press the button of my BottomNavigation a couple of times and for each of this clicks a new Fragment would be instantiated. Also the BottomNavigation buttons are not set according to the Fragments. I found this answer but it didn't work out Prevent The Same Fragment From Stacking More Than Once ( addToBackStack)
You can use following structure to have the same working logic of Instagram. First create a custom limited unique queue class. It only holds last N items, where N is the limit number (or maximum item count) passed to its constructor. Moreover, this type of queue keeps only one instance of a class. If an instance of class A is already in the queue and another instance of class A is to be added to the queue, the former instance is removed first and then the new object is inserted. public class LimitedUniqueQueue<E> extends LinkedList<E> { private int limit; public LimitedUniqueQueue(int limit) { this.limit = limit; } #Override public boolean add(E o) { // For uniqueness for (int i = 0; i < super.size(); i++) { E item = super.get(i); if (item.getClass() == o.getClass()) { super.remove(i); break; } } boolean added = super.add(o); // For size limit while (added && size() > limit) { super.remove(); } return added; } } Then update your Activity as follows: public class MainActivity extends AppCompatActivity { private BottomNavigationView navigation; private LimitedUniqueQueue<Fragment> queue; ... private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() { Fragment gameFragment; Fragment tournamentFragment; Fragment profileFragment; #Override public boolean onNavigationItemSelected(#NonNull MenuItem item) { FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); switch (item.getItemId()) { case R.id.navigation_game: if (gameFragment == null) { gameFragment = new GameFragment(); } transaction.replace(R.id.content, gameFragment).commit(); queue.add(gameFragment); return true; case R.id.navigation_tournament: if (tournamentFragment == null) { tournamentFragment = new TournamentFragment(); } transaction.replace(R.id.content, tournamentFragment).commit(); queue.add(tournamentFragment); return true; case R.id.navigation_profile: if (profileFragment == null) { profileFragment = new ProfileFragment(); } transaction.replace(R.id.content, profileFragment).commit(); queue.add(profileFragment); return true; } return false; } }; #Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); navigation = (BottomNavigationView) findViewById(R.id.navigation); queue = new LimitedUniqueQueue<>(navigation.getMenu().size()); navigation.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener); navigation.setSelectedItemId(R.id.navigation_game); ... } #Override public void onBackPressed() { if (queue.size() > 1) { queue.removeLast(); Fragment previousFragment = queue.getLast(); if (previousFragment instanceof GameFragment) { navigation.setSelectedItemId(R.id.navigation_game); } else if (previousFragment instanceof TournamentFragment) { navigation.setSelectedItemId(R.id.navigation_tournament); } else if (previousFragment instanceof ProfileFragment) { navigation.setSelectedItemId(R.id.navigation_profile); } } else { super.onBackPressed(); } } ... }
How to handle bottom navigation perfectly with back pressed
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
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: Know how many fragments there are in the stack before the commit If the fragment is not "home type", save it to the stack before the commit 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); } } }); }
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); }
#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); } }
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 :)
onBackPressed did not worked for me. So this I used. #Override protected void onResume() { super.onResume(); bottomNavigationView.getMenu().getItem(0).setChecked(true); }
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); } }
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!
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(); }
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); } }}
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(); }
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);
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
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); } } });
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(); } } }
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
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); }
Late answer but here it goes. Let's say you have a BottomNavigation inside MainActivity with 4 Fragments. FragmentA FragmentB FragmentC 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.
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); } }
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); }
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() } } }
This is how I solved my, 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 } }