Maintaining BackStack In Bottom Navaigation - android

I am using bottomNavigation in my android app. I have 5 fragments A, B, C, D, E. The fragment B contains another fragment says B1. When I backpress on fragment B1, it closes the app. How can I move to fragment B when backpressed from fragment B1.
Here is my code
bottomNavigationView.setOnNavigationItemSelectedListener(
item -> {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.A:
fragment = new A();
break;
case R.id.B:
fragment = new B();
break;
case R.id.C:
fragment = new C();
break;
case R.id.D:
fragment = new D();
break;
case R.id.E:
fragment = new E();
break;
}
return loadFragment(fragment);
});
private boolean loadFragment(Fragment fragment) {
//switching fragment
if (fragment != null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.main_fragment, fragment)
.commit();
return true;
}
return false;
}

While opening B1 Fragment use this
private boolean loadFragmentB1(Fragment fragment) {
//switching fragment
if (fragment != null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.main_fragment, fragment)
.commit();
return true;
While opening other fragment except B1 , use this
private boolean loadFragment(Fragment fragment) {
//switching fragment
if (fragment != null) {
getSupportFragmentManager()
.beginTransaction()
.addToBackStack(null)
.replace(R.id.main_fragment, fragment)
.commit();
return true;
}
return false;
}
And on activity override onBackPressed like this
#Override
public void onBackPressed(){
if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
finish();
}
}

Related

Adding back button to fragment

I have a bottom navigation bar that contains 4 fragments and when a tab is selected a new instance of that fragment is loaded.
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
Fragment fragment = null;
switch (item.getItemId()) {
case R.id.navigation_home:
fragment = HomeFragment.newInstance();
break;
case R.id.navigation_cards:
fragment = CardsFragment.newInstance();
break;
case R.id.navigation_deals:
fragment = DealsFragment.newInstance();
break;
case R.id.navigation_settings:
fragment = SettingsFragment.newInstance();
break;
}
if (fragment != null) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.content, fragment);
fragmentTransaction.commit();
}
return true;
}
};
Now in my HomeFragment there is a RecyclerView that On Item Select it opens a new Fragment:
myViewHolder.cardView.setOnClickListener(view -> {
System.out.println("clicked");
Fragment fragment = new TargetDetailsFragment();
FragmentTransaction ft = ((AppCompatActivity) context).getSupportFragmentManager()
.beginTransaction();
ft.replace(R.id.content, fragment).addToBackStack(null);
ft.commit();
});
I want to add a back button to the TargetDetails Fragments that takes you back to the home page when selected and I attempted doing that by implementing OnBackStackChangedListener in the Main activity
#Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canback = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canback);
}
#Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
}
but the problem is when I click on it its reloads the HomeFragment Again but I simply want it to go back to the saved instance of that Fragment
Here is my code that I have used for the ViewPager, the idea is to see if the current page number is 0 than to proceed super.onBackPressed(); otherwise go to the previous fragment:
#Override
public void onBackPressed() {
if(vpPager.getCurrentItem()!=0) {
vpPager.setCurrentItem(vpPager.getCurrentItem()-1, true);
} else {
super.onBackPressed();
}
}
By adding below code in your Activity.
The fragment back stack can be managed.
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
Hello #Bolu Okunaiya i think you should try this it will help you to manage backstack to desired fragment without loading same fragment again.
For Stop loading previous fragment you should use "add" instead of "replace" with your FragmentTransaction
Inside your MainActivity
#Override
public void onBackStackChanged() {
//shouldDisplayHomeUp();
Fragment currentFragment = getActivity().getFragmentManager()
.findFragmentById(R.id.fragment_container);
if (currentFragment instanceof TargetDetails) {
Log.v(TAG, "your current fragment is TargetDetails");
popBackStackImmediate(); //<<<< immediate parent fragment will open,which is your HomeFragement
}
}
To use popBackStackImmediate() you need to *replace FragmentA with FragmentB and use addToBackstack() before commit().

How to handle onBackPressed() in multiple fragment

I am Android beginner, I am developing an app in which I have used multiple fragments through an activity.
private void displaySelectedScreen(int itemId) {
Fragment fragment = null;
switch (itemId) {
case R.id.nav_home:
fragment = new HomeFragment();
break;
case R.id.nav_attendance:
fragment = new AttendanceFragment();
break;
case R.id.nav_notification:
fragment = new NotificationFragment();
break;
case R.id.nav_event:
fragment = new EventFragment();
break;
case R.id.nav_fee:
fragment = new FeesFragment();
break;
case R.id.nav_news:
fragment = new NewsFragment();
break;
}
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.frame_container, fragment);
ft.addToBackStack(null);
ft.commit();
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
}
and use an onBackPressed() method to close an app if the drawer is already close. Now I want to close an app if current fragment is HomeFragment and if current fragment is not HomeFragment then replace by HomeFragment. I don't know, how it does?
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout)findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
closeApp();
}
}
You can add tag to your fragment. in this way you will only have to check the tag name.
Fragment fragment = new Fragment();
getFragmentManager().beginTransaction()
.replace(R.id.FrameLayoutId,fragment,"TAG_NAME")
.addToBackStack("SOURCE_TAG_NAME").commit();
This is how to check your fragment tag, put this checking on your backpress method on the activity or in your closeApp() method
function void closeApp(){
Fragment fragment = getFragmentManager().findFragmentByTag("HomeActivity");
if (fragment != null) {
if(fragment.isVisible()){
HomeActivity.this.finish();
}
}
}
Use this code onBackPressed() method in NavigationActivity. I hope that can solve you.
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else {
Fragment f = getVisibleFragment();// to get which fragment now
if (f.getClass() != MainFragment.class) {
Fragment fragment = null;
try {
fragment = (Fragment) MainFragment.class.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.frame, fragment).commit(); // this is your framelayout
}else {
super.onBackPressed();
}
}
}
getVisibleFragment() function
public Fragment getVisibleFragment() {
FragmentManager fragmentManager = NavigationActivity.this.getSupportFragmentManager();
List<Fragment> fragments = fragmentManager.getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment != null && fragment.isVisible())
return fragment;
}
}
return null;
}
For simple, just go to your HomeFragment.java and edit the method onBackPressed() to exit the application using finish().
And for others Fragment.java, just use popBackStack() function, you can search for it here :
https://developer.android.com/reference/android/app/FragmentManager.html#popBackStack()
In your MainActivity class override onBackPressed and display dialogbox there like this:
#Override
public void onBackPressed() {
//show dialog box here
}
In each Fragment implement onResume() method with following code:
#Override
public void onResume() {
super.onResume();
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
//homefragment class name
Class fragmentClass = HomeFragment.class;
try {
Fragment fragment = (Fragment) fragmentClass.newInstance();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction().replace(R.id.flContent, fragment).commit();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
return false;
}
});
}

android Fragment popBackStack confusion

I have a lot of Fragments. e.g: A1,A2,A3, B1,B2,C1,C2,C3. All Fragments were in a container,FActivity.
I hoped when current Fragment was A1 or B1 or c1 I press the back key,the project finished.
I follwed this order to hide previous one and show next one, and all of them were addToBackStack.But when I call FActivity.onBackPressed(), I found the popBack not restoring previous fragment and feel confused.some fragment were missed.Especially B1 and C1 were not in the backstack. They were missed.
here is my hide&show code:
private void showFragment(FragmentTransaction ft1, Fragment fm1, Fragment fm2, String tag) {
if (fm1 != null) {
if (!fm1.isAdded()) {
ft1.add(R.id.information_detail, fm1).hide(fm1);
} else {
ft1.hide(fm1);
}
ft1.addToBackStack(tag);
}
if (!fm2.isAdded()) {
ft1.add(R.id.infomation_detail, fm2, tag);
} else {
ft1.show(fm2);
}
ft1.commit();
}
fragment control
Fragment currentFragment ;
public void startFragmentOnParameter(Fragment oldFragment, String tag, String... param) {
FragmentTransaction ft1 = getFragmentManager().beginTransaction();
switch (tag) {
case "A1":
A1= new A1();
showFragment(ft1,oldFragment, A1, true, "A1");
currentFragment = mPatientCaseInHistory;
break;
case "A2":
A2= new A2();
showFragment(ft1, oldFragment, A2, false, "A2");
currentFragment = mPatientWithArrear2;
break;
case "A3":
A3= new A3();
showFragment(ft1, oldFragment, A3, false, "A3");
currentFragment = mPatientsCases3;
break;
......
}
and onBackPressed
public void onBackPressed() {
String name = currentFragment.getClass().getSimpleName();
if(name.equals("A1")||name.equals("B2")||name.equals("C1")){
finish();
}else{
super.onBackPressed();
currentFragment = (Fragment) getFragmentManager().findFragmentById(R.id.infomation_detail);
}
}
What's wrong in my code?
Try to use
getFragmentManager().popBackStack();
instead
super.onBackPressed();

addToBackStack not working with onBackPressed method

I have created a tabbed activity inside a navigation drawer activity. I have linked the lists inside the navigation drawer with individual fragments. I also used the command ft.addToBackStack(null) before ft.commit(). It was perfectly fine and I was able to use it to close the fragments inside the navigation drawer. now I have created webviews inside the fragments of the tabbed activity (home page). I added the onBackPressed to go back inside the webview and now the addToBackStack method stopped working.
For the navigation drawer fragments,
private void dispaySelectedScreen(int id) {
Fragment fragment = null;
switch (id) {
case R.id.fragment1:
fragment = new Fragment1();
break;
case R.id.fragment2:
fragment = new Fragment2();
break;
case R.id.fragment3:
fragment = new Fragment3();
break;
case R.id.fragment4:
fragment = new fragment4();
break;
}
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_main, fragment);
ft.addToBackStack(null);
ft.commit();
}
onBackPressed Method
#Override
public void onBackPressed() {
if (hWebView.canGoBack()) {
hWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (sWebView.canGoBack()) {
sWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (dWebView.canGoBack()) {
dWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate());
}
Please help me out!!!
try this code: override onBackPressed() in your tabbed activity (home page).
#Override
public void onBackPressed() {
FragmentManager fm = getSupportFragmentManager();
if (hWebView.canGoBack()) {
hWebView.goBack();
} else if (!getFragmentManager().popBackStackImmediate())
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
} else {
super.onBackPressed();
}
}

How to keep only one instance of a fragment, when switching with NavigationDrawer?

My App starts with a AddressFragment. From the NavigationDrawer I start (amongst others) a new AddressFragment with:
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new AddressFragment())
.addToBackStack(null)
.commit();
But I would rather just go back the first instance. How could I do that?
Or more general, how can I find out, whether an instance of a fragment already exists and then start that, if yes, otherwise create one?
When creating the fragment set a tag for it, then later you can find it through the fragment manager and replace/create accordingly.
FragmentManager fManager = getFragmentManager();
FragmentTransaction fTransaction = fManager.beginTransaction();
Fragment fragment = fManager.findFragmentByTag("uniqueTag");
// If fragment doesn't exist yet, create one
if (fragment == null) {
fTransaction.add(R.id.fragment_list, new ListFrag(), "uniqueTag");
}
else { // re-use the old fragment
fTransaction.replace(R.id.fragment_list, fragment, "uniqueTag");
}
Step one:
Optimize current code to allow a Fragment to have his own "TAG"
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, new AddressFragment())
.addToBackStack(**AddressFragment.class.getName()**)
.commit();
Step two:
Somewhere in your application flow you will need to determine if a fragment exists:
public static boolean isFragmentInBackstack(final FragmentManager fragmentManager, final String fragmentTagName) {
for (int entry = 0; entry < fragmentManager.getBackStackEntryCount(); entry++) {
if (fragmentTagName.equals(fragmentManager.getBackStackEntryAt(entry).getName())) {
return true;
}
}
return false;
}
Step three:
perform fragment action
if (exists) {
// Fragment exists, go back to that fragment
//// you can also use POP_BACK_STACK_INCLUSIVE flag, depending on flow
mFragmentManager.popBackStackImmediate(AddressFragment.class.getName(), 0);
} else {
// Fragment doesn't exist
// STEP 1 + additional backstack management
}
This is tested answer, Hope this will help you
First Make these field globally in MainActivity
private static final int TIME_INTERVAL = 2000;
private long mBackPressed;
private FragmentManager fm;
private FragmentTransaction ft;
private DrawerLayout drawer;
Now in onNavigationItemSelected() implement like that
#Override
public boolean onNavigationItemSelected(MenuItem item) {
Fragment fragment = null;
Class fragmentClass = null;
switch (item.getItemId()) {
case R.id.nav_home:
fragmentClass = Fragment.class;//this is MainAvctivty extends AppCompatActivity
break;
case R.id.nav_f1:
fragmentClass = FragmentOne.class;
break;
case R.id.nav_f2:
fragmentClass = FragmentTwo.class;
break;
case R.id.nav_f3:
fragmentClass = FragmentThree.class;
break;
case R.id.nav_f4:
fragmentClass = FragmentFour.class;
break;
case R.id.nav_f5:
fragmentClass = FragmentFive.class;
break;
default:
fragmentClass = Fragment.class;
}
try {
fragment = (Fragment) fragmentClass.newInstance();
} catch (Exception e) {e.printStackTrace();}
//Insert the fragment by replacing any existing fragment
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
ft.replace(R.id.frame_container, fragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
item.setChecked(true);
setTitle(item.getTitle());
drawer.closeDrawers();
return true;
}
Now handle onbackpressed like below
#Override
public void onBackPressed() {
if (drawer != null) {
fm = getSupportFragmentManager();
ft = fm.beginTransaction();
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
ft.commit();
} else if (mBackPressed + TIME_INTERVAL > System.currentTimeMillis()) {
super.onBackPressed();
} else {
Toast.makeText(this,"Press again to exit the app",Toast.LENGTH_SHORT).show();
}
mBackPressed = System.currentTimeMillis();
}
}
First time you create the fragment with a tag.
When you want to replace it try to get the fragment with tag and if it return null you create a new one

Categories

Resources