Fragment is not visible after popbackstack - android

I add fragment (home fragment) in Activity.onCreate(), without adding it to backstack, I do it using FragmentTransaction.replace(). After that i add every next fragment using FragmentTransaction.replace() and adding it to backstack.
I have a functionality that clears the backstack, leaving home fragment visible, to do it I use FragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);.
If i add only 1 fragment to backstack and then clear the backstack it works fine, but if I add more than one then after clearing the backstack the screen is blank, though the home fragment receives onStart() and onResume() and think's it's visible, it doesnt receive onPause(), onStop() until I add another fragment again or leave the app.
Also if I popBackstack() one by one (on user action) it works fine, but if I popBackstack() in loop (popping all fragments at once) it doesnt work.
Here's the code:
public void changeFragment(BaseFragment fragment, boolean addToBackStack, boolean preventDuplicate) {
Fragment topFragment = getSupportFragmentManager().findFragmentById(fragmentContainer.getId());
if (preventDuplicate && topFragment != null && fragment.getClass().equals(topFragment.getClass())) {
//Prevent adding same fragment
return;
}
FragmentTransaction transaction =
fragmentManager
.beginTransaction()
.replace(fragmentContainer.getId(), fragment);
if (addToBackStack) {
transaction.addToBackStack(null);
}
transaction.commit();
}
public void goToHome() {
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}

There is a new Method for FragmentTransaction (added in version 25.1.0) see: https://developer.android.com/reference/android/support/v4/app/FragmentTransaction.html#setAllowOptimization(boolean).
Try setting this on the FragmentTransaction while :
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setAllowOptimization(true);
transaction.replace(...);
This fixed the issue for me.
Credits to: https://stackoverflow.com/a/42597413/5310016

Turns out new support library version is messing things up. After changing
compile 'com.android.support:appcompat-v7:25.1.1'
back to
compile 'com.android.support:appcompat-v7:25.0.1'
everything works fine

Related

popBackStackImmediate not showing fragment when the current fragment transaction is not added in the backstack

Popbackstack is working fine when all the fragments in the sequence are added in the backstack but isnt working when one of the transactions is not added in the backstack.
Here is my navigation:
1.Replace fragment to load home fragment. This transaction not added to backstack.
Replace fragment to load login fragment. This transaction is added to backstack.
3.Replace fragment to load loggedin fragment. This transaction is not added to backstack.
Now, when i press back button once nothing happens. Whereas ideally it should go to the home fragment from logged in fragment.
Here is my onbackpressed method in main activity:
#Override
public void onBackPressed() {
if(getSupportFragmentManager().getBackStackEntryCount()>0)
{
FragmentManager.BackStackEntry backStackEntry = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
String str = backStackEntry.getName();
FragmentManager fm=getSupportFragmentManager();
//getSupportFragmentManager().popBackStackImmediate();
fm.popBackStack(str, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
else {
super.onBackPressed();
}
}
popBackstack only 'pop' what is in the backstack.
Since you haven't add the transaction when replacing the LoginFragment by the LoggedInFragment when you press back:
the LoggedInFragment remains,
the LogInFragment is popped
the HomeFragment is displayed
But because the LoggedInFragment as been added after the HomeFragment, the HomeFragment is displayed underneath it. So you can't see it as hidden by the LoggedInFragment.
One solution is to add the transaction to the back stack when you replace the LogInFragment by the LoggedInFragment.
Then in onBackPressed you test if the current fragment is the LoggedInFragment. If it's the case you pop the back stack up to HomeFragment (not inclusive). Like that both LoggedInFragment and LogInFragment will be pop.
EDIT
#Override
public void onBackPressed() {
FragmentManager manager = getSupportFragmentManager();
Fragment fragment = manager.findFragmentById(R.id.my_fragment_container);
// If there is something in the back stack AND the current fragment is the LoggedInFragment
if (manager.getBackStackEntryCount() > 0
&& fragment instanceof LoggedInFragment) {
manager.popBackStack(HomeFragment.class.getSimpleName(), 0);
} else {
super.onBackPressed();
}
}
In order to retrieve the HomeFragment by name you need to tag your transaction when you replace the current fragment by the HomeFragment. Generally I tag all transactions with the fragment's class simple name so like that I can retried any fragment:
transaction.replace(R.id.my_fragment_container, fragment, fragment.getClass().getSimpleName());
Eselfar's explanation of the problem is correct, but the code he provided wasn't generic enough for me.
I (hopefully) resolved this issue in a general case by the following code:
#Override
public void onBackPressed() {
Fragment currentFragment = getCurrentFragment();
if (mFragmentManager.getBackStackEntryCount() > 0) {
// In a normal world, just popping back stack would be sufficient, but since android
// is not normal, a call to popBackStack can leave the popped fragment on screen.
// Therefore, we start with manual removal of the current fragment.
removeCurrentFragment();
mFragmentManager.popBackStack();
} else {
super.onBackPressed();
}
}
private Fragment getCurrentFragment() {
return mFragmentManager.findFragmentById(getContentFrameId());
}
private void removeCurrentFragment() {
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.remove(getCurrentFragment());
ft.commit();
// not sure it is needed; will keep it as a reminder to myself if there will be problems
// mFragmentManager.executePendingTransactions();
}

Android reload fragments stack on restore

I’m developing an android application that makes heavy use of fragments, I’m running into an issue and I’ve been unable to find a solution so far.
The flow is this: the app is launched and MainActivity is the first responder, now, depending on user interaction several fragments gets loaded and pushed onto the stack.
Here is an example:
Main Activity -> fragment A -> fragment B -> fragment C -> etc..
Back history is enabled like so:
fragment C -> fragment B -> fragment A -> etc..
Everything works perfectly fine as long as my application is in foreground but everything breaks when the application goes in background.
If I’m on fragment B for example and I press the home button the application goes in background and when I restore it back it starts from MainActivity with fragment A.
Also, the toolbar shows the title of fragment B and, since fragment A contains a recyclerview I can see parts of fragment B between item rows, like a background image.
This is how I load fragments:
public void loadFragment(Fragment fragment, Boolean addToStack) {
// Load fragment
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_container, fragment);
fragmentTransaction.addToBackStack(null);
// show back button
if (addToStack) {
// Code to show the back button.
}
else if (fragmentManager.getBackStackEntryCount() > 0 && !addToStack) {
hideBackButton();
fragmentManager.popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
fragmentTransaction.commitAllowingStateLoss();
}
I call this function from MainActivity and from the fragments:
MyFragment theFragment = new MyFragment();
MainActivity.instance.loadFragment(theFragment, true);
What I want to achieve is that when the application is restored it gets straight to the previously loaded fragment, keeping the entire "back" history. How can I do this?
I'm not sure if other portions of code are needed, but if so I'll post them as required.
call the onResume() function:
public void onResume(){
Fragment frg = null;
frg = getSupportFragmentManager().findFragmentByTag("Your_Fragment_TAG");
final FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.detach(frg);
ft.attach(frg);
ft.commit();
}
Ok so, after a lot of research I found the issue… and the issue was a mistake of mine.
I’d like to report it here for everyone that might run into the same issue.
At first I tried to force fragment replacement on onResume() function like so:
Fragment f = getSupportFragmentManager().findFragmentById(R.id.container);
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.frame_container, f);
fragmentTransaction.addToBackStack(null);
However android should handle all of this automatically, at least in my knowledge, and so I tried to dig further and I finally narrowed it down to my onStart() method.
Basically I was registering the EventBus and making a function call
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
UserNetworkManager userNetworkManager = new UserNetworkManager(MainActivity.mainActivity);
userNetworkManager.fetchFeed();
}
This code was creating the issue and after all it was not necessary to put it there, so I moved it to the onCreate() method, cleaned up my code a little bit and everything works fine now.

Android FragmentManager calls onCreateView while clearing fragments

I have these methods for fragment manipulation:
public void replaceFragment(Fragment fragment) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
}
public void addFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.main_frame, fragment);
if (addToBackStack) {
fragmentTransaction.addToBackStack(fragment.getClass().getName());
}
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
}
public void clearFragments() {
if (fragmentManager.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry first = fragmentManager.getBackStackEntryAt(0);
fragmentManager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
Scenario:
I start with fragment A, when activity is created. addFragment(new A, false);
Then I add fragment B. replaceFragment(new B);
I add another fragment C. addFragment(new C);
In C fragment I want to reset application to A fragment. I call clearFragments(); addFragment(new A, false);
The problem is that B fragment onCreateView is called after pressing button on C fragment that clears fragments and adds A fragment. I don't see the fragment, but it starts background thread that crashes my app. I need to clear all fragments without calling their onCreateView() methods.
Is this expected behaviour, or am I doing something wrong?
Actually in such a scenario you will always return to the fragment B.
Step by step:
Adding A.
Replacing A with B <- after this step you don't have A within your activity.
Adding C. Here you are adding another fragment and telling FragmentManager to preserve current state of fragments to the back stack. Your current state at this point is fragment B. Then fragment C is added.
When you call clearFragments, FragmentManager returns to the first saved state - and this state is fragment B.
Maybe you forgot to mention something? I tried to put several entries in a back stack and then reset to the first state - onCreateView is called once, only for the first fragment.

getFragmentManager().popbackstack() causing nullpointer exception

I am developing an android app with lots of fragments in it. But I am facing problems in switching between them.
Lets say I have a Fragment A now I want to go to Fragment B, for this I am doing like this--
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager
.beginTransaction();
Fragment fragment = new Fragment_B();
transaction.add(R.id.frameLayout, fragment);
transaction.addToBackStack(null);
transaction.commit();
I reach Fragment B successfully. Now I have to go to Fragment C from here, for this again I am doing the same thing.
FragmentManager manager = getFragmentManager();
FragmentTransaction transaction = manager
.beginTransaction();
Fragment fragment = new Fragment_C();
transaction.add(R.id.frameLayot, fragment);
transaction.addToBackStack(null);
transaction.commit();
I do this successfully also. Now I have to revert back to Fragment B, for this I do -
getFragmentManager.popbackstack();
This brings me back to Fragment B. But when I do the same to go to Fragment A now, it causes NullpointerException.
What I am doing wrong here. Why does this run perfectly for the first time but fails at the second time? Please help.
I think you have to check first there are fragment available in backstack or not. follow my below code:
if(manager.getBackStackEntryCount()>0){
manager.popBackStack();
manager.beginTransaction().commit();
}
Thats it...
and yes you do not add fragment a to backstack initally so crosscheck that..
fragmentTransaction.addToBackStack("tag");
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 1) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}

Removing a Fragment from the back stack

I have a 3 fragments in an activity when the a tablet is held in portrait. However I only have 2 of these fragments when in landscape. The problem I am having is when going from portrait to landscape the activity is creating the 3rd fragment. I receive and error as this fragment cannot be created.
I have worked out that this fragment is being created because it is in the back stack.
I have tried to remove the fragment in the onDestroy method by using
FragmentTransaction f = fragmentManager.beginTransaction();
f.remove(mf);
f.commit();
However the I get an error saying that I cannot use this function after the onSaveInstanceState
What would be the correct way of taking this fragment out of the back stack?
Update
I should probably add that the fragment I am having problems with is a mapFragment from this libary
https://github.com/petedoyle/android-support-v4-googlemaps
The way I use it is like so
mf = MapFragment.newInstance(1, true);
ft = fragmentManager.beginTransaction();
ft.replace(R.id.mapContainer, mf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.addToBackStack("map");
ft.commit();
You add to the back state from the FragmentTransaction and remove from the backstack using FragmentManager pop methods:
FragmentManager manager = getActivity().getSupportFragmentManager();
FragmentTransaction trans = manager.beginTransaction();
trans.remove(myFrag);
trans.commit();
manager.popBackStack();
I created a code to jump to the desired back stack index, it worked fine to my purpose.
ie. I have Fragment1, Fragment2 and Fragment3, I want to jump from Fragment3 to Fragment1
I created a method called onBackPressed in Fragment3 that jumps to Fragment1
Fragment3:
public void onBackPressed() {
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.popBackStack(fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-2).getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
In the activity, I need to know if my current fragment is the Fragment3, so I call the onBackPressed of my fragment instead calling super
FragmentActivity:
#Override
public void onBackPressed() {
Fragment f = getSupportFragmentManager().findFragmentById(R.id.my_fragment_container);
if (f instanceof Fragment3)
{
((Fragment3)f).onBackPressed();
} else {
super.onBackPressed();
}
}
you show fragment in a container (with id= fragmentcontainer) so you remove fragment with:
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragmentContainer);
fragmentTransaction.remove(fragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
In case you ended up here trying to figure out how to remove fragment(s) from backstack and you are using Android Jetpack Navigation component you could just use app:popUpTo and app:popUpToInclusive in action node of navigation graph xml to specify fragments that you want to pop out of back stack.
https://developer.android.com/guide/navigation/navigation-navigate#pop
Check this too
https://stackoverflow.com/a/51974492/4594986
What happens if the fragment that you want to remove is not on top of the stack?
Then you can use theses functions
popBackStack(int arg0, int arg1);
popBackStack(String arg0, int arg1);

Categories

Resources