So this is the code I have for my bottom navigation bar, in my tabs I'm retrieving data from parse
my question is what do I put inside my BottomNavigationView.OnNavigationItemReselectedListener in order for the tab that I reselect to reload/refresh
would very much appreciate any help!
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new UsersFragment();
final Fragment fragment3 = new ProfileFragment();
final Fragment fragment4 = new SettingsFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
#Override
public void onBackPressed() {
if(ParseUser.getCurrentUser() != null){
Intent a = new Intent(Intent.ACTION_MAIN);
a.addCategory(Intent.CATEGORY_HOME);
a.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(a);
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_home);
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(navigationItemSelectedListener);
bottomNavigationView.setOnNavigationItemReselectedListener(navigationItemReselectedListener);
fm.beginTransaction().add(R.id.fragment_container, fragment4, "4").hide(fragment4).commit();
fm.beginTransaction().add(R.id.fragment_container, fragment3, "3").hide(fragment3).commit();
fm.beginTransaction().add(R.id.fragment_container, fragment2, "2").hide(fragment2).commit();
fm.beginTransaction().add(R.id.fragment_container,fragment1, "1").commit();
}
private BottomNavigationView.OnNavigationItemSelectedListener navigationItemSelectedListener = new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull #NotNull MenuItem item) {
switch (item.getItemId()){
case R.id.nav_home:
fm.beginTransaction().hide(active).show(fragment1).commit();
active = fragment1;
return true;
case R.id.nav_list:
fm.beginTransaction().hide(active).show(fragment2).commit();
active = fragment2;
return true;
case R.id.nav_profile:
fm.beginTransaction().hide(active).show(fragment3).commit();
active = fragment3;
return true;
case R.id.nav_settings:
fm.beginTransaction().hide(active).show(fragment4).commit();
active = fragment4;
return true;
}
return false;
}
};
private BottomNavigationView.OnNavigationItemReselectedListener navigationItemReselectedListener = new BottomNavigationView.OnNavigationItemReselectedListener() {
#Override
public void onNavigationItemReselected(#NonNull #NotNull MenuItem item) {
}
};
}```
So you are calling the fragment through bottom navigation bar
So for every selection of an item in the bottom navigation yo do want to refresh the fragment
Do use attach() and detach() method while beginning the transaction. It refreshes each time
I need help to combine ViewPager and BottomNavigationView. But when I swipe the screen as ViewPager works, the fragment become stacked with another fragment. How do I fix this?
Stacked fragment
On normal fragment before swiped
Here is my code :
MainActivity.java
public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {
private BottomNavigationView mBottomNavigation;
private ViewPager viewPager;
private ViewPagerAdapter mViewPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadFragment(new HomeFragment());
mBottomNavigation = findViewById(R.id.buttom_navigation);
mBottomNavigation.setOnNavigationItemSelectedListener(this);
viewPager = findViewById(R.id.view_pager);
mViewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mViewPagerAdapter);
}
private boolean loadFragment(Fragment fragment) {
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container_frame_layout, fragment);
ft.commit();
return true;
}
return false;
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
Fragment fragment = null;
switch (menuItem.getItemId()) {
case R.id.menu_home:
fragment = new HomeFragment();
break;
case R.id.menu_favorite:
fragment = new FavoriteFragment();
break;
case R.id.menu_account:
fragment = new AccountFragment();
break;
}
return loadFragment(fragment);
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/container_frame_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<androidx.viewpager.widget.ViewPager
android:id="#+id/view_pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/buttom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/colorPrimary"
android:layout_alignParentBottom="true"
app:itemIconTint="#ffff"
app:itemTextColor="#ffff"
app:menu="#menu/item_menu"/>
</RelativeLayout>
ViewPagerAdapter.java
public class ViewPagerAdapter extends FragmentStatePagerAdapter {
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new HomeFragment();
case 1:
return new FavoriteFragment();
case 2:
return new AccountFragment();
}
return null;
}
#Override
public int getCount() {
return 3;
}
Thanks in advance!
Change this code in MainActivity
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch (menuItem.getItemId()) {
case R.id.menu_home:
viewPager.setCurrentItem(0);
break;
case R.id.menu_favorite:
viewPager.setCurrentItem(1);
break;
case R.id.menu_account:
viewPager.setCurrentItem(2);
break;
}
return true;
}
Add this code for select bottomNavigation Tab.
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected(int position) {
switch (position) {
case 0:
mBottomNavigation.getMenu().findItem(R.id.menu_home).setChecked(true);
break;
case 1:
mBottomNavigation.getMenu().findItem(R.id.menu_favorite).setChecked(true);
break;
case 2:
mBottomNavigation.getMenu().findItem(R.id.menu_account).setChecked(true);
break;
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
});
As the answer of Mr. #Chintan , I replaced :
return new FooFragment();
to
viewPager.setCurrentItem(index);
And remove loadFragment() function, it's perfectly works now. Thank you very much!
binding.bottomNavigation.setOnClickMenuListener(model -> {
switch (model.getId()) {
case 1:
binding.viewPager.setCurrentItem(0);
break;
case 2:
binding.viewPager.setCurrentItem(1);
break;
case 3:
binding.viewPager.setCurrentItem(2);
break;
case 4:
binding.viewPager.setCurrentItem(3);
break;
}
return null;
});
I'm trying to have a navigation drawer that replaces a frame layout with a fragment on a navigation item click. Here's my code for the drawer...
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
case R.id.things_manager:
ThingsManager thingsManager = new ThingsManager();
FragmentManager fragmentManager1 = getSupportFragmentManager();
fragmentManager1.beginTransaction().replace(R.id.fragment_container, thingsManager).commit();
return true;
case R.id.settings_tab:
Settings settings = new Settings();
FragmentManager fragmentManager2 = getSupportFragmentManager();
fragmentManager2.beginTransaction().replace(R.id.fragment_container, settings).commit();
return true;
case R.id.sign_out_tab:
Intent i = new Intent(this, Login.class);
startActivity(i);
return true;
}
return super.onOptionsItemSelected(item);
}
The nav menu pops up when I click the menu icon, but none of the item clicks work. Not even the sign out intent that I have at the bottom.
#Override
public boolean onOptionsItemSelected(MenuItem item) {
android.support.v4.app.Fragment myFragment = null;
Class fragmentClass = null;
switch (item.getItemId()) {
case android.R.id.home:
//Example if your fragment activity name is fragment_home
fragmentClass = fragment_home.class;
break;
case R.id.things_manager:
//Same as Home
break;
case R.id.settings_tab:
//
break;
case R.id.sign_out_tab:
//
break;
}
try{
myFragment = (android.support.v4.app.Fragment) fragmentClass.newInstance();
}catch(Exception e){
e.printStackTrace();
}
android.support.v4.app.FragmentManager fragmentManager = getSupportFragmentManager();
//Back Button Wont Kill
fragmentManager.beginTransaction().replace(R.id.frameLayout, myFragment).addToBackStack(null).commit();
return super.onOptionsItemSelected(item);
}
Seeing that you have Drawer already implemented I assume you have already declared NavigationView in your Activity. If haven't already.
Implement OnNavigationItemSelectedListener in your Activity.
public class MyActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {....}
Add this in your onCreate of Activity
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
Add this method in your Activity to handle clicks on Drawer items.
#Override
public boolean onNavigationItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
mDrawerLayout.openDrawer(GravityCompat.START);
return true;
case R.id.things_manager:
ThingsManager thingsManager = new ThingsManager();
FragmentManager fragmentManager1 = getSupportFragmentManager();
fragmentManager1.beginTransaction().replace(R.id.fragment_container, thingsManager).commit();
return true;
case R.id.settings_tab:
Settings settings = new Settings();
FragmentManager fragmentManager2 = getSupportFragmentManager();
fragmentManager2.beginTransaction().replace(R.id.fragment_container, settings).commit();
return true;
case R.id.sign_out_tab:
Intent i = new Intent(this, Login.class);
startActivity(i);
return true;
}
mDrawerLayout.closeDrawer(GravityCompat.START);
return true;
}
I would like to make a bottom navigation bar in my project. Every view has it's own fragment. The problem is that every time i click on the button to change the view for example from recents to favorites it creates new fragment with completely new states(e.g. scroll position, text changed whatever my fragment contains). I know that in official Android documentation there was written that bottom navigation bar should reset the task states, but i think it is too uncomfortable for users.
I would like to have kind of similar functionality like instagram that you change from feed to explore and back to feed the scroll position the image caches everything remains saved. I tried almost every way to solve this problem the only thing that worked is by setting visibility GONE and setting visibility VISIBLE according to situation but i understand that it is not RIGHT way there should be better way of doing this and i am not talking about manually saving needed instances. I followed almost every tutorial about bottom nav fragments but the interesting thing is that no one is interested to make it use without calling new every time.
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, FirstFragment.newInstance());
fragmentTransaction.commit();
bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.menu_dialer:
fragment = FirstFragment.newInstance();
break;
case R.id.menu_email:
fragment = SecondFragment.newInstance();
break;
case R.id.menu_map:
fragment = ThirdFragment.newInstance();
break;
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit();
}
return true;
}
});
I also tried the onAttach and Deattach solutions but again no success.
VIDEO LINK : new i tried Nino Handler version it only works when i tap on the same fragment button
Maybe it is connected that i am using canary version or something wrong in my gradle dependencies?
NEW UPDATES:
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private FragmentManager fragmentManager;
private Fragment currentFragment;
String TAG = "babken";
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
Fragment fragment = null;
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
break;
case R.id.navigation_dashboard:
fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
if (fragment == null) {
fragment = FragmentSecond.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_TWO);
break;
}
return true;
}
};
private void replaceFragment(#NonNull Fragment fragment, #NonNull String tag) {
if (!fragment.equals(currentFragment)) {
fragmentManager
.beginTransaction()
.replace(R.id.armen, fragment, tag)
.commit();
currentFragment = fragment;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FragmentFirst.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
}
I had similar issue, but this code solved my problem.
public class MainActivity extends AppCompatActivity {
boolean doubleBackToExitPressedOnce = false;
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new DashboardFragment();
final Fragment fragment3 = new NotificationsFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
BottomNavigationView navigation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
setFragment(fragment1, "1", 0);
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
setFragment(fragment1, "1", 0);
return true;
case R.id.navigation_dashboard:
setFragment(fragment2, "2", 1);
return true;
case R.id.navigation_notifications:
setFragment(fragment3, "3", 2);
return true;
}
return false;
}
};
public void setFragment(Fragment fragment, String tag, int position) {
if (fragment.isAdded()) {
fm.beginTransaction().hide(active).show(fragment).commit();
} else {
fm.beginTransaction().add(R.id.main_container, fragment, tag).commit();
}
navigation.getMenu().getItem(position).setChecked(true);
active = fragment;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
if (active == fragment1) {
if (doubleBackToExitPressedOnce) {
super.onBackPressed();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
} else {
setFragment(fragment1, "1", 0);
}
}
}
I wouldn't keep the fragment instances globally.
Instead add a tag to the fragment when creating them
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, new PlaceholderFragment(), TAG_PLACEHOLDER)
.commit();
Then you can always retrieve it like this:
Fragment fragment = getSupportFragmentManager().findFragmentByTag(TAG_PLACEHOLDER);
if (fragment == null) {
fragment = new PlaceholderFragment();
}
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.container, fragment, TAG_PLACEHOLDER)
.commit();
UPDATE: I updated my answer and to provide a complete solution:
private static final String TAG_FRAGMENT_ONE = "fragment_one";
private static final String TAG_FRAGMENT_TWO = "fragment_two";
private static final String TAG_FRAGMENT_THREE = "fragment_three";
private FragmentManager fragmentManager;
private Fragment currentFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// instantiate the fragment manager
fragmentManager = getSupportFragmentManager();
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FirstFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
bottomNavigationView = (BottomNavigationView) findViewById(R.id.navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.menu_dialer:
// I'm aware that this code can be optimized by a method which accepts a class definition and returns the proper fragment
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_ONE);
if (fragment == null) {
fragment = FirstFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_ONE);
break;
case R.id.menu_email:
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_TWO);
if (fragment == null) {
fragment = SecondFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_TWO);
break;
case R.id.menu_map:
Fragment fragment = fragmentManager.findFragmentByTag(TAG_FRAGMENT_THREE);
if (fragment == null) {
fragment = ThirdFragment.newInstance();
}
replaceFragment(fragment, TAG_FRAGMENT_THREE);
break;
}
return true;
}
});
}
private void replaceFragment(#NonNull Fragment fragment, #NonNull String tag) {
if (!fragment.equals(currentFragment)) {
fragmentManager
.beginTransaction()
.replace(R.id.frameLayout, fragment, tag)
.commit();
currentFragment = fragment;
}
}
ADDITIONAL INFO: If you want to be sure that the fragment states don't change and if you also want to be able to swipe the fragments you should consider using a ViewPager with a FragmentStatePagerAdapter and change the current fragment in the adapter with every click event
I wrote a Kotlin Extension function for FragmentManager class
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()
}
This can be called by supportFragmentManager.swtich(R.id.container,newFrag,newFrag.TAG) from onNavigationItemSelected
All the previous answers are using fragmentTransaction.replace(...). This will replace the current fragment by destroying it (which is causing the problem). Therefore all those solutions won't actually work.
This is the closest thing I could get to as a solution for this problem:
private void selectContentFragment(Fragment fragmentToSelect)
{
FragmentTransaction fragmentTransaction = this.getSupportFragmentManager().beginTransaction();
if (this.getSupportFragmentManager().getFragments().contains(fragmentToSelect)) {
// Iterate through all cached fragments.
for (Fragment cachedFragment : this.getSupportFragmentManager().getFragments()) {
if (cachedFragment != fragmentToSelect) {
// Hide the fragments that are not the one being selected.
fragmentTransaction.hide(cachedFragment);
}
}
// Show the fragment that we want to be selected.
fragmentTransaction.show(fragmentToSelect);
} else {
// The fragment to be selected does not (yet) exist in the fragment manager, add it.
fragmentTransaction.add(R.id.fragment_container, fragmentToSelect);
}
fragmentTransaction.commit();
}
To make this work, you should keep track of the fragments in an array (or in separate variables) in your Activity. I for reference pre-instantiated all fragments in a SparseArray.
You can use attach() and detach() methods:
private FirstFragment firstFragment = FirstFragment.newInstance();
private SecondFragment secondFragment= SecondFragment.newInstance();
private ThirdFragment thirdFragment = ThirdFragment.newInstance();
navigation.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_dialer:
changeFragment(firstFragment, "firstFragment");
return true;
case R.id.menu_email:
changeFragment(secondFragment, "secondFragment");
return true;
case R.id.menu_map:
changeFragment(thirdFragment, "thirdFragment");
return true;
}
return false;
}
});
public void changeFragment(Fragment fragment, String tagFragmentName) {
FragmentManager mFragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
Fragment currentFragment = mFragmentManager.getPrimaryNavigationFragment();
if (currentFragment != null) {
fragmentTransaction.detach(currentFragment);
}
Fragment fragmentTemp = mFragmentManager.findFragmentByTag(tagFragmentName);
if (fragmentTemp == null) {
fragmentTemp = fragment;
fragmentTransaction.add(R.id.content, fragmentTemp, tagFragmentName);
} else {
fragmentTransaction.attach(fragmentTemp);
}
fragmentTransaction.setPrimaryNavigationFragment(fragmentTemp);
fragmentTransaction.setReorderingAllowed(true);
fragmentTransaction.commitNowAllowingStateLoss();
}
So I have been researching for this for a long time and figured out that there is no way to reuse fragment with AUTOMATICALLY saved states you have to save your needed states manually then retrieve them whenever a new fragment is created but what about scroll position it is too hard and even in some cases it is impossible to save scroll view position state(e.g. recycler view). So i used the concept called VISIBILITY when i click on the button that fragment becomes visible others hide automatically.
How about that.
You declare the fragments in the class.
Fragment firstFragment,secondFragment,thirdFragment;
then in the switch-case you can code as that:
switch (item.getItemId()) {
case R.id.menu_dialer:
if(firstFragment != null) {
fragment = firstFragment;
}else{
fragment = FirstFragment.newInstance();
}
break;
case R.id.menu_email:
// the same ...
break;
case R.id.menu_map:
//the same ...
break;
}
Thomas's answer nearly helped me but had an issue that whenever I open new fragments the very first time, they overlapped but doesn't get overlapped once I open them again by pressing the menu buttons.
So I modified his code and obtained the solution using the following code:
private fun selectContentFragment(fragmentToSelect: Fragment) {
val fragmentTransaction = fragmentManager?.beginTransaction()
if (fragmentManager?.fragments?.contains(fragmentToSelect)!!) {
// Show the fragment that we want to be selected.
fragmentTransaction?.show(fragmentToSelect)
} else {
// The fragment to be selected does not (yet) exist in the fragment manager, add it.
fragmentTransaction?.add(R.id.container, fragmentToSelect)
}
// Iterate through all cached fragments.
for (cachedFragment in fragmentManager?.fragments!!) {
if (cachedFragment !== fragmentToSelect) {
// Hide the fragments that are not the one being selected.
// Uncomment following line and change the name of the fragment if your host isn't an activity and a fragment otherwise whole view will get hidden.
// if (!cachedFragment.toString().contains("HomeContainerFragment"))
fragmentTransaction?.hide(cachedFragment)
}
}
fragmentTransaction?.commit()
}
Make sure you're not passing the fragment's new instance every time.
This will work:
selectContentFragment(
when (item.itemId) {
R.id.home -> frag1
R.id.photoGallery -> frag2
else -> Home()
}
)
where frag1 and frag2 are global variables defined as:
val frag1 = Home()
val frag2 = PhotoGallery()
This will not work:
selectContentFragment(
when (item.itemId) {
R.id.home -> Home()
R.id.photoGallery -> PhotoGallery()
else -> Home()
}
)
It wasted my several hours. Hope it helps others!
**This code very helpful for me. Just like youtube. **
private Deque<Integer> fragmentIds = new ArrayDeque<>(3);
int itemId;
private HomeFragment homeFragment = new HomeFragment();
private FavouriteFragment favouriteFragment = new FavouriteFragment();
private NearmeFragment nearmeFragment = new NearmeFragment();
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentIds.push(R.id.action_home);
showTabWithoutAddingToBackStack(homeFragment);
bottomNavigationView = findViewById(R.id.bottom_navigation);
bottomNavigationView.setOnNavigationItemSelectedListener(onNavigationItemClicked);
}
private BottomNavigationView.OnNavigationItemSelectedListener onNavigationItemClicked = new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
itemId= item.getItemId();
if(fragmentIds.contains(itemId)){
fragmentIds.remove(itemId);
}
fragmentIds.push(itemId);
showTabWithoutAddingToBackStack(getFragment(item.getItemId()));
return true;
}
};
private Fragment getFragment(int fragmentId) {
switch (fragmentId) {
case R.id.action_home:
return homeFragment;
case R.id.action_favorites:
return favouriteFragment;
case R.id.action_nearme:
return nearmeFragment;
}
return homeFragment;
}
private void showTabWithoutAddingToBackStack(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, fragment.getClass().getSimpleName()).commit();
}
#Override
public void onBackPressed() {
if(fragmentIds.getLast() != R.id.action_home){
fragmentIds.addLast(R.id.action_home);
}
fragmentIds.pop();
bottomNavigationView.getMenu().getItem(fragmentIds.size()-1).setChecked(true);
if (!fragmentIds.isEmpty()) {
showTabWithoutAddingToBackStack(getFragment(fragmentIds.peek()));
} else {
finish();
}
}
For Kotlin user may help this code :
First create a FragmentManager extension class
fun FragmentManager.replace(containerId: Int, fragment: 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 = fragment
add(containerId, current!!, tag)
} else {
show(current!!)
}
}
.setTransition(FragmentTransaction.TRANSIT_ENTER_MASK)
.setPrimaryNavigationFragment(current)
.setReorderingAllowed(true)
.commitNowAllowingStateLoss()
}
Now simply call it on your
onNavigationItemSelected
supportFragmentManager.replace(R.id.fragment_id,YourFragmentClass,yourFragment.TAG)
I had the same issue but I resolved it by creating multiple fragment hosts then I added fragments in fragmentHost. And when bottom nav itemSelected I just make the required host fragment visible and other host fragmentsHost to visibility gone.
This method may be wrong but it works perfectly for me.
And we don't have to manually handle the states of fragment only we need to handle backpress of activity.
But this method does not pause the fragments.
And I don't know how to handle the pauses and resumes of fragments so please reply to me.
Maybe This will help you.
This is my fragmentHosts Home Activity:
<androidx.fragment.app.FragmentContainerView
android:id="#+id/homeFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/libraryFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/myStuffFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<androidx.fragment.app.FragmentContainerView
android:id="#+id/moreFragmentHost"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottom_NavBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:visibility="gone"/>
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="#+id/bottom_NavBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:menu="#menu/app_bottom_nav_menu"
tools:ignore="BottomAppBar" />
Home Activity onCreate:
supportFragmentManager.beginTransaction().replace(R.id.homeFragmentHost,HomeFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.libraryFragmentHost,LibraryFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.myStuffFragmentHost,MyStuffFragment()).commitNow()
supportFragmentManager.beginTransaction().replace(R.id.moreFragmentHost,MoreFragment()).commitNow()
homeFragmentHost.visibility = View.VISIBLE
Bottom nav Item Selected listener
override fun onNavigationItemSelected(item: MenuItem): Boolean {
return when(item.itemId){
R.id.homeFragment -> loadFragmentHost(homeFragmentHost)
R.id.libraryFragment -> loadFragmentHost(libraryFragmentHost)
R.id.myStuffFragment -> loadFragmentHost(myStuffFragmentHost)
R.id.moreFragment -> loadFragmentHost(moreFragmentHost)
else -> false
}
loadFragmentHost function
private fun loadFragmentHost(view:FragmentContainerView): Boolean {
val list = arrayListOf(homeFragmentHost,libraryFragmentHost,myStuffFragmentHost,moreFragmentHost)
list.remove(view)
view.visibility = View.VISIBLE
list.forEach {
it.visibility = View.GONE
}
return true
}
public class MainActivity extends AppCompatActivity {
boolean doubleBackToExitPressedOnce = false;
final Fragment fragment1 = new HomeFragment();
final Fragment fragment2 = new DashboardFragment();
final Fragment fragment3 = new NotificationsFragment();
final FragmentManager fm = getSupportFragmentManager();
Fragment active = fragment1;
BottomNavigationView navigation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
setFragment(fragment1, "1", 0);
}
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
setFragment(fragment1, "1", 0);
return true;
case R.id.navigation_dashboard:
setFragment(fragment2, "2", 1);
return true;
case R.id.navigation_notifications:
setFragment(fragment3, "3", 2);
return true;
}
return false;
}
};
public void setFragment(Fragment fragment, String tag, int position) {
if (fragment.isAdded()) {
fm.beginTransaction().hide(active).show(fragment).commit();
} else {
fm.beginTransaction().add(R.id.main_container, fragment, tag).commit();
}
navigation.getMenu().getItem(position).setChecked(true);
active = fragment;
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main_menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
startActivity(new Intent(MainActivity.this, SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
if (active == fragment1) {
if (doubleBackToExitPressedOnce) {
super.onBackPressed();
return;
}
this.doubleBackToExitPressedOnce = true;
Toast.makeText(this, "Please click BACK again to exit", Toast.LENGTH_SHORT).show();
} else {
setFragment(fragment1, "1", 0);
}
}
}
Maintain Bottom sheet fragment Reusable
BackPress Maintain
double back press to exit
public class MainActivity extends AppCompatActivity {
BottomNavigationView bottomNavigationView;
Toaster toaster;
private final Fragment androidFragment = new AndroidFragment();
private final Fragment settingsFragment = new SettingsFragment();
Fragment active;
String TAG = MainActivity.class.getSimpleName();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toaster = new Toaster(this);
bottomNavigationView = findViewById(R.id.bottom_navigation);
renderFragment(androidFragment, "android");
active = androidFragment;
bottomNavigationView.setOnItemSelectedListener(
item -> {
Log.e(TAG, "onCreate: " + active );
if(item.getItemId() == R.id.action_android){
renderFragment(androidFragment, "android");
return true;
}
else if(item.getItemId() == R.id.action_settings){
renderFragment(settingsFragment, "settings");
return true;
}
return false;
}
);
}
private void renderFragment(Fragment fragment, String tag){
FragmentManager fragmentManager = getSupportFragmentManager();
if(fragment.isAdded()){
fragmentManager.beginTransaction().hide(active).show(fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
}
else {
if(active != null){
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container_view, fragment, tag)
.hide(active)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
}
else {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container_view, fragment, tag)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN).commit();
}
}
active = fragment;
}
}
This worked for me Guys.
Create the three fragments as members of the class and reuse them.
public class MainActivity extends AppCompatActivity {
private final Fragment mFirstFragment = new FirstFragment();
private final Fragment mSecondFragment = new SecondFragment();
private final Fragment mThirdFragment = new ThirdFragment();
#Override
protected void onCreate(Bundle savedInstanceState) {
......
......
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, mFirstFragment);
fragmentTransaction.commit();
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
if (bottomNavigationView.getSelectedItemId() != item.getItemId()) {
switch (item.getItemId()) {
R.id.menu_dialer:
replaceFragment(mFirstFragment);
break;
case R.id.menu_email:
replaceFragment(mSecondFragment);
break;
case R.id.menu_map:
replaceFragment(mThirdFragment);
break;
}
}
return true;
}
});
}
private void replaceFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.frameLayout, fragment);
fragmentTransaction.commit();
}
}