BottomNavigationView - How to avoid recreation of Fragments and reuse them - java

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();
}
}

Related

How do I refresh when I reselect the tab I'm on in my bottom navigation bar?

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

How can I retain Fragments and their data on both orientation change using BottomNavigationView

After following a couple of Youtube tutorials (eg https://www.youtube.com/watch?v=tPV8xA7m-iw) I was able to build an Activity with a BottomNavigationView, that switches between three different fragments.
I'm not sure if this is the best method to achieve my goals. I need three screens, one of which (ReportFragment) will have user entered data that I wish to remaining place as the user switches between fragments.
The difficulty I am having is also retaining the Fragment data on an orientation change.
Here is one of the Fragment codes, I have implemented onSavedInstanceState to recall the data the user has entered and it seems to work okay:
public class ReportFragment extends Fragment {
EditText textCheck;
EditText textCheck2;
EditText textCheck3;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_report, null);
textCheck = view.findViewById(R.id.editName);
textCheck2 = view.findViewById(R.id.editHaz);
textCheck3 = view.findViewById(R.id.editNotes);
if (savedInstanceState != null) {
textCheck.setText(savedInstanceState.getString("name"));
textCheck2.setText(savedInstanceState.getString("haz"));
textCheck3.setText(savedInstanceState.getString("notes"));
}
return view;
}
#Override
public void onSaveInstanceState (Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putString("name", textCheck.getText().toString());
outState.putString("haz", textCheck2.getText().toString());
outState.putString("notes", textCheck3.getText().toString());
}
}
Here is the Activity Code:
public class FeedActivity extends AppCompatActivity
implements BottomNavigationView.OnNavigationItemSelectedListener {
private HomeFragement frag1;
private ReportFragment frag2;
private MapFragement frag3;
BottomNavigationView navigation;
private static final String TAG = "FeedActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feed);
navigation = findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(this);
if (savedInstanceState == null) {
frag1 = new HomeFragement();
frag2 = new ReportFragment();
frag3 = new MapFragement();
loadFragment(frag1);
}
}
private boolean loadFragment(Fragment fragment) {
if (fragment != null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
//.addToBackStack(null)
.commit();
return true;
}
return false;
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = frag1;
break;
case R.id.navigation_camera:
fragment = frag2;
break;
case R.id.navigation_map:
fragment = frag3;
break;
}
return loadFragment(fragment);
}
}
I have tried a couple of ways in an attempt to retain the original Fragment, one being onSavedInstanceState, as per other suggestions on Stack Overflow. However during execution I receive an error regarding a null reference to navigation:
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("opened_fragment", navigation.getSelectedItemId());
}
as well as using the following code up in the OnCreate Method in in the if(onSavedInstanceState == null) statement:
navigation.setSelectedItemId(savedInstanceState.getInt("opened_fragment"));
The app currently correctly switches between the three fragments and retains the data as it needs to, until an orientation change occurs. The displayed fragment reverts back to frag1, and can no longer switch between fragments (I'm guessing it is something to do with the setOnNavigationItemSelectedListener??).
I am new to Android development.
EDIT: Now using a ViewPager which handles the the issues above
I believe I have found a solution myself. It appears to work, and give the functionality I need. However I would love some feedback from any experts as to whether it is the right solution.
The solution uses the fragment 'tag' assigned when using getSupportFragmentManager().replace, and checks if there has been a previous version of the fragment loaded. I'm not confident my logic in onNavigationItemSelected() is great but would love any feedback/criticism
Updated FeedActivity.Java:
public class FeedActivity extends AppCompatActivity
implements BottomNavigationView.OnNavigationItemSelectedListener {
private ReportFragment frag2;
private MapFragement frag3;
private HomeFragement frag1;
BottomNavigationView navigation;
private static final String TAG = "FeedActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_feed);
navigation = findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(this);
if(savedInstanceState==null) {
frag1 = new HomeFragement();
frag2 = new ReportFragment();
frag3 = new MapFragement();
}
loadFragment(frag1, "home");
}
private boolean loadFragment(Fragment fragment, String tagID){
if(fragment != null){
getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left)
.replace(R.id.fragment_container, fragment, tagID)
.addToBackStack(null)
.commit();
return true;
}
return false;
}
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
String tagID = null;
switch(item.getItemId()){
case R.id.navigation_home:
Fragment cachedFragment1 = getSupportFragmentManager().findFragmentByTag("home");
if (cachedFragment1 != null){
fragment = cachedFragment1;
}
else{
fragment = new HomeFragement();
}
tagID = "home";
break;
case R.id.navigation_camera:
Fragment cachedFragment2 = getSupportFragmentManager().findFragmentByTag("cam");
if (cachedFragment2 != null){
fragment = cachedFragment2;
}
else{
fragment = new ReportFragment();
}
tagID = "cam";
break;
case R.id.navigation_map:
Fragment cachedFragment3 = getSupportFragmentManager().findFragmentByTag("map");
if (cachedFragment3 != null){
fragment = cachedFragment3;
}
else{
fragment = new MapFragement();
}
tagID = "map";
break;
}
return loadFragment(fragment, tagID);
}}

onBackPressed and backStack showing blank screen before exit

Earlier, I was having an issue with onBackPressed and got it resolved with some help here.
After doing some research, I found out that due to having my starting fragment in the backstack, it's producing an extra 'back' count so when I attempt to exit my app, I get a blank screen:
Instead of the blank screen I should get a prompt for "Are you sure to exit" instead of the blank. From the blank screen, I do get my 'are you sure to exit' menu, but I'd prefer not seeing an extra blank screen.
I've also noticed that it seems to not update the fragment titles on the back pressed, but I'm assuming that's another issue all together.
I'm unsure how to separate my home fragment from the backStack count. If I'm to avoid it from being added to the stack, but leave it as a navigational item, I'm confused on how that would even work.
Code:
public class Home extends AppCompatActivity
implements NavigationView.OnNavigationItemSelectedListener {
private Fragment fragment = null;
private FragmentManager fragmentManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
DatabaseHelper myDbHelper = new DatabaseHelper(Home.this);
try {
myDbHelper.createDataBase();
} catch (IOException ioe) {
throw new Error("Unable to create database");
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.addDrawerListener(toggle);
toggle.syncState();
NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
navigationView.setItemIconTintList(null);
// getHome();
displayView(R.id.nav_large_monsters);
}
private boolean viewIsAtHome;
#Override
public void onBackPressed() {
// Pressing back popped the back stack, nothing else to do
FragmentManager fragmentManager = getSupportFragmentManager();
if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) {
return;
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
}
if (!viewIsAtHome) {
displayView(R.id.nav_large_monsters);
} else {
new AlertDialog.Builder(this)
.setMessage("Are you sure you want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Home.this.finish();
}
})
.setNegativeButton("No", null)
.show();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.search_menu, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
return super.onOptionsItemSelected(item);
}
private void getHome(){
fragment = new HomeFragment();
if (fragment != null) {
FragmentManager fragmentMgmt = getSupportFragmentManager();
fragmentMgmt.beginTransaction().replace(R.id.content_frame, fragment).commit();
}
}
#SuppressWarnings("StatementWithEmptyBody")
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
displayView(item.getItemId());
return true;
}
public void displayView(int viewId) {
// Fragment fragment = null;
String title = getString(R.string.app_name);
switch (viewId) {
default:
fragment = new Large_Monsters();
title = "Large Monsters";
break;
case R.id.nav_large_monsters:
fragment = new Large_Monsters();
title = "Large Monsters";
viewIsAtHome = true;
break;
case R.id.nav_small_monsters:
fragment = new Small_Monsters();
title = "Small Monsters";
viewIsAtHome = false;
break;
case R.id.nav_weapons:
fragment = new Weapons();
title = "Weapons";
viewIsAtHome = false;
break;
case R.id.nav_armors:
fragment = new Armors_Low_High();
title = "Armor Sets";
viewIsAtHome = false;
break;
case R.id.nav_charms:
fragment = new Charms();
title = "Charms";
viewIsAtHome = false;
break;
case R.id.nav_items:
fragment = new Items();
title = "Items";
viewIsAtHome = false;
break;
case R.id.palico_armor:
fragment = new Palico_Armor();
title = "Palico Armor";
viewIsAtHome = false;
break;
case R.id.palico_gadgets:
fragment = new Palico_Gadgets();
title = "Palico Gadgets";
viewIsAtHome = false;
break;
case R.id.palico_weps:
fragment = new Palico_Weapons();
title = "Palico Weapons";
viewIsAtHome = false;
break;
case R.id.palico_helms:
fragment = new Palico_Helms();
title = "Palico Helmets";
viewIsAtHome = false;
break;
}
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment, fragment.getTag());
ft.addToBackStack(null);
ft.commit();
}
// set the toolbar title
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(title);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
}
}
You just don't have to call addToBackStack(null) on your Home page(ex: Large_Monsters fragment)
so just add in your onCreate method like this
Large_Monsters fragment = new Large_Monsters();
if (fragment != null) {
FragmentManager fragmentMgmt = getSupportFragmentManager();
fragmentMgmt.beginTransaction().replace(R.id.content_frame, fragment).commit();
}
In your left side menu, you just need to check whether this fragment exists or not, if not then replace it otherwise remove other fragments.
I think I figured it out just after posting this...
The section here in the onBackPressed():
if (!viewIsAtHome) {
displayView(R.id.nav_large_monsters);
}
Was there to help set the check for what's the home page, but IF I use my getHome() method, and set that under OnCreate:
private void getHome(){
fragment = new Large_Monsters();
if (fragment != null) {
FragmentManager fragmentMgmt = getSupportFragmentManager();
fragmentMgmt.beginTransaction().replace(R.id.content_frame, fragment).commit();
}
}
This let's me set my home page, and avoids the double check at onBackPressed. But mainly, this avoids setting a check at my onNavigationItemSelected area and let's android handle onBackPressed as usual.

Change BottomNavigationView Icons on Back Button clicked

At the bottom of my layout I have a BottomNavigationView with three fragments. If I click the back button the fragment is switching but not the bottom icons. How can I fix it?
addToBackStack() works. Maybe you have some advise to pretty the code. Is it good practise to have the fragment tags in the activity or fragment?
public class MainActivity extends AppCompatActivity {
private FragmentManager mFragmentManager;
private BottomNavigationView mBottomNavigationView;
private static final String HOME_FRAGMENT = "homeFragment";
private static final String SEARCH_FRAGMENT = "searchFragment";
private static final String SHARE_FRAGMENT = "shareFragment";
private boolean isFirstFragment;
private long mBackPressedTime;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
setBottomNavigationView();
}
private void init() {
mBottomNavigationView = findViewById(R.id.bottomNavigationView);
mFragmentManager = getSupportFragmentManager();
mBackPressedTime = 0;
}
private void setBottomNavigationView() {
setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
isFirstFragment = true;
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.ic_home:
setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
return true;
case R.id.ic_search:
setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
return true;
case R.id.ic_circle:
setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
return true;
default:
return false;
}
}
});
}
private void setFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment, tag);
if (isFirstFragment) {
transaction.addToBackStack(tag);
}
transaction.commit();
}
#Override
public void onBackPressed() {
final long currentTimeMillis = System.currentTimeMillis();
if (mFragmentManager.getBackStackEntryCount() > 0) {
mFragmentManager.popBackStack();
} else if (currentTimeMillis - mBackPressedTime > 2000) {
mBackPressedTime = currentTimeMillis;
Toast.makeText(this, getString(R.string.reach_homescreen), Toast.LENGTH_SHORT).show();
} else {
super.onBackPressed();
}
}
}
#Hans Baum instead of adding your first fragment to back stack try this code,
#Override
public void onBackPressed() {
if(mBottomNavigationView.getSelectedItemId () != R.id.ic_home)
{
mBottomNavigationView.setSelectedItemId(R.id.ic_home);
}
else
{
super.onBackPressed();
}
}
This code will exit your activity if you are in Home Fragment else if you are in any other Fragment it will go to Home Fragment.
So no addToBackStack() needed. So your serFragment() method should be,
private void setFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment, tag);
transaction.commit();
}
hope it helps.
Note that in your code you never assigned false to isFirstFragment so i assume all fragments are added to backstack which is very memory consuming.
UPDATE:
Since your requirement is different.As you want Instagram like tabs, i hope this implementation helps you .
Deque<Integer> mStack = new ArrayDeque<>();
boolean isBackPressed = false;
private void setBottomNavigationView() {
mBottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.ic_home:
if(!isBackPressed) {
pushFragmentIntoStack(R.id.ic_home);
}
isBackPressed = false
setFragment(HomeFragment.newInstance(), HOME_FRAGMENT);
return true;
case R.id.ic_search:
if(!isBackPressed) {
pushFragmentIntoStack(R.id.ic_search);
}
isBackPressed = false
setFragment(SearchFragment.newInstance(), SEARCH_FRAGMENT);
return true;
case R.id.ic_circle:
if(!isBackPressed) {
pushFragmentIntoStack(R.id.ic_circle);
}
isBackPressed = false
setFragment(ShareFragment.newInstance(), SHARE_FRAGMENT);
return true;
default:
return false;
}
}
});
mBottomNavigationView.setOnNavigationItemReselectedListener(new
BottomNavigationView.OnNavigationItemReselectedListener() {
#Override
public void onNavigationItemReselected(#NonNull MenuItem item) {
}
});
mBottomNavigationView.setSelectedItemId(R.id.ic_home);
pushFragmentIntoStack(R.id.ic_home);
}
private void pushFragmentIntoStack(int id)
{
if(mStack.size() < 3)
{
mStack.push(id);
}
else
{
mStack.removeLast();
mStack.push(id);
}
}
private void setFragment(Fragment fragment, String tag) {
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment, tag);
transaction.commit();
}
#Override
public void onBackPressed() {
if(mStack.size() > 1)
{
isBackPressed = true;
mStack.pop();
mBottomNavigationView.setSelectedItemId(mStack.peek());
}
else
{
super.onBackPressed();
}
}
I have used deque to store the order in which the tabs are clicked Since there are 3 tabs deque size is 3 .On back press it will pop the stack and go to that tab. If no item in stack it will quit the activity.
IMPORTANT NOTE:
Do not add fragments to backstack in this scenario because as i switch the tabs the backstack count will keep increasing and may even lead to MemoryOutOfBound exception.
To Question 2:
Coming to your Fragment tag question , It is good to save tag in fragment . As the fragment is reusable in any activity instead adding tag in every activity it is used , it is good if we add it in fragment itself.
Try this:
#Override
public void onBackPressed() {
if(navigation_bottom.getSelectedItemId () != R.id.action_home)
{
FragmentTransaction transaction =
((HomeActivity)this).getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.frameLayout_home, new HomeFragment());
transaction.commit();
navigation_bottom.setSelectedItemId(R.id.action_home);
}
else
{
HomeActivity.this.finishAffinity();
}
}
You can add the fragment name in the backstack with the fragment transaction
getFragmentManager()
.beginTransaction()
.replace(R.id.frame, YourCurrentFragment.newInstance())
.addToBackStack(YourFragment.class.getName())
.commit();
Then you can add onBackstackChangedListener to get the currently selected fragment
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
FragmentManager.BackStackEntry bse = fragmentManager.
getBackStackEntryAt(fragmentManager.getBackStackEntryCount() -1);
//bse will be the backstack entry for current fragment
if (bse.getName().equals(YourFragment.class.getName())) {
navigationView.getMenu().getItem(0).setChecked(true);
} else if (bse.getName().equals(YourSecondFragment.class.getName())) {
navigationView.getMenu().getItem(1).setChecked(true);
}
}
});

BottomNavigationView Back button not working properly

When is press back button my fragment is changing to home page, but bottom icon is not changing. I posted my all code here. If i select more then two navigation button from bottom navigation view items then when i press back button it'll redirect to last selected button item.
But now what happening is that suppose i'm on the last item say 'Notification' [as you can see in screenshot], now when i press back button it'll directly take me to 'Home' button, but what i want is that it should take me first to 'Search' button item and then to 'Home' not directly to 'home'.
How can i achieve this??
Here is the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:design="http://schemas.android.com/apk/res-auto"
android:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.segunfamisa.sample.bottomnav.MainActivity">
<FrameLayout
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#f1f1f1">
</FrameLayout>
<android.support.design.widget.BottomNavigationView
android:id="#+id/navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="start"
design:menu="#menu/bottom_nav_items" />
</LinearLayout>
Here is the activity:
public class MainActivity extends AppCompatActivity {
private static final String SELECTED_ITEM = "arg_selected_item";
private BottomNavigationView mBottomNav;
private int mSelectedItem;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBottomNav = (BottomNavigationView) findViewById(R.id.navigation);
mBottomNav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
selectFragment(item);
return true;
}
});
MenuItem selectedItem;
if (savedInstanceState != null) {
mSelectedItem = savedInstanceState.getInt(SELECTED_ITEM, 0);
selectedItem = mBottomNav.getMenu().findItem(mSelectedItem);
} else
{
selectedItem = mBottomNav.getMenu().getItem(0);
}
selectFragment(selectedItem);
}
#Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt(SELECTED_ITEM, mSelectedItem);
super.onSaveInstanceState(outState);
}
#Override
public void onBackPressed() {
MenuItem homeItem = mBottomNav.getMenu().getItem(0);
if (mSelectedItem != homeItem.getItemId()) {
// select home item
// homeItem.setCheckable(true);
Log.d("backpressids","**** "+mSelectedItem);
selectFragment(homeItem);
} else {
super.onBackPressed();
}
}
private void selectFragment(MenuItem item) {
Fragment frag = null;
item.setCheckable(true);
// init corresponding fragment
switch (item.getItemId()) {
case R.id.menu_home:
TabFragmentOne fragmentone = new TabFragmentOne();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, fragmentone);
ft.commit();
break;
case R.id.menu_notifications:
TabFragmentThree fragmentone_Three = new TabFragmentThree();
FragmentTransaction ft_three = getSupportFragmentManager().beginTransaction();
ft_three.replace(R.id.container, fragmentone_Three);
ft_three.commit();
break;
case R.id.menu_search:
TabFragmentTwo tabFragmentTwo = new TabFragmentTwo();
FragmentTransaction ft_two = getSupportFragmentManager().beginTransaction();
ft_two.replace(R.id.container,tabFragmentTwo);
ft_two.commit();
break;
}
// update selected item
mSelectedItem = item.getItemId();
updateToolbarText(item.getTitle());
if (frag != null) {
TabFragmentOne fragmentone = new TabFragmentOne();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, fragmentone);
ft.commit();
}
}
private void updateToolbarText(CharSequence text) {
ActionBar actionBar = getSupportActionBar();
if (actionBar != null) {
actionBar.setTitle(text);
}
}
}
please help me any one.....
You can use BottomNavigationView.setSelectedItemId(ITEM_ID) to select desired MenuItem:
In onBackPressed(), use mBottomNav.setSelectedItemId(R.id.menu_home) or mBottomNav.setSelectedItemId(homeItem.getItemId()).
Update onBackPressed() method as below:
#Override
public void onBackPressed() {
MenuItem homeItem = mBottomNav.getMenu().getItem(0);
if (mSelectedItem != homeItem.getItemId()) {
selectFragment(homeItem);
// Select home item
mBottomNav.setSelectedItemId(homeItem.getItemId());
} else {
super.onBackPressed();
}
}
Hope this will help~
If you want to go back to all the items on the stack (not only home), try the following code:
FragmentTransaction transaction = mFragmentManager.beginTransaction();
transaction.replace(R.id.container, fragment);
if (!isFirstFragment) {
transaction.addToBackStack(null);
}
transaction.commit();
Then, add this code onBackPressed:
#Override
public void onBackPressed() {
int count = getSupportFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
} else {
int index = ((getSupportFragmentManager().getBackStackEntryCount()) -1);
getSupportFragmentManager().popBackStack();
FragmentManager.BackStackEntry backEntry = getSupportFragmentManager().getBackStackEntryAt(index);
int stackId = backEntry.getId();
mBottomNavigationView.getMenu().getItem(stackId).setChecked(true);
}
}
I want to share my solution. I just added a counter checkBackSteck
binding.bnb - this is buttonNavigationView
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private val checkBackStack = ArrayList<Int>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
checkBackStack.add(0)
val mainFragment = MainFragment()
val libraryFragment = LibraryFragment()
val menuFragment = MenuFragment()
val statisticFragment = StatisticFragment()
val profileFragment = ProfileFragment()
binding.apply {
bnb.setOnItemSelectedListener {
when(it.itemId){
R.id.bnb_home -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.fragment_in, R.anim.fragment_out)
.replace(R.id.fragmentContainerView, mainFragment)
.addToBackStack(null)
.commit()
checkBackStack.add(0)
true
}
R.id.bnb_library -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.fragment_in, R.anim.fragment_out)
.replace(R.id.fragmentContainerView, libraryFragment)
.addToBackStack(null)
.commit()
checkBackStack.add(1)
true
}
R.id.bnb_menu ->{
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.fragment_in, R.anim.fragment_out)
.replace(R.id.fragmentContainerView, menuFragment)
.addToBackStack(null)
.commit()
checkBackStack.add(2)
true
}
R.id.bnb_statistic -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.fragment_in, R.anim.fragment_out)
.replace(R.id.fragmentContainerView, statisticFragment)
.addToBackStack(null)
.commit()
checkBackStack.add(3)
true
}
R.id.bnb_profile -> {
supportFragmentManager.beginTransaction()
.setCustomAnimations(R.anim.fragment_in, R.anim.fragment_out)
.replace(R.id.fragmentContainerView, profileFragment)
.addToBackStack(null)
.commit()
checkBackStack.add(4)
true
}
else -> false
}
}
}
}
override fun onBackPressed() {
if (checkBackStack.size == 1){
super.onBackPressed()
} else {
supportFragmentManager.popBackStack()
binding.bnb.menu.getItem(checkBackStack.get(checkBackStack.count() - 2)).isChecked = true
checkBackStack.removeAt(checkBackStack.count() - 1)
}
}
}

Categories

Resources