I want to use a Support library to add a new L-styled ActionBar to my app. So I changed my activity to an ActionBarActivity. Now I have both getFragmentManager() and getSupportFragmentManager().
I use getFragmentManager() to work with fragments and they are rendered OK.
But there is a problem when I add transaction to backstack ((mTransaction.addToBackStack("blabla"))). Backstack remains empty so if I call mTransaction.commit() and right after getFragmentManager().getBackStackEntryCount() latter will return 0 no matter how many transaction I commit. As a result my app will be closed at first "bacK" press without navigation to previous fragment.
Why I don't use Fragments from Support Library?
App is targeted to API 15 and newer so I don't care about <11 and there is lot of code to migrate. Also if use Support Library i won't be able to use objectAnimator for transition. It isn't disaster, but I just don't want to degrade one part of app to improve another.
So the question - is it possible to fix back stack or i should migrate all fragment-related code to support-v4?
EDIT: I was posting too fast before reading your question carefully.
I've looked through the source code of FragmentActivity and Activity and I highly recommend to use getSupportFragmentManager() if using ActionBarActivity.
The reason is that the FragmentManager implementation mFragments is shadowed in FragmentActivity and is used all over the activity life cycle. If using getFragmentManager() the super classes implementation will be accessed. And you will have to rely on FragmentActivity to make the super calls at appropriate times, else the life cycle might get messed up. With onBackPressed() you've already discovered a case where FragmentActivity fails to call super.
Another solution would be to migrate to Toolbar. But you'd loose some of the goodies of ActionBarActivity e.g. widget-tinting and ChildFragmentManager.
I'd just stick with the support implementation.
This is the original answer and doesn't really apply here:
Committing a fragment transaction, does not immediately add/replace your fragment. Therefore your back stack will still be empty. You can call getFragmentManager().executePendingTransactions() if this is really necessary.
Btw: Why would using the support library prevent you from using ObjectAnimator ?
OK, after few more test I found that actually back stack is filled but it is not handled by activity.
Adding following code solved my problem but i'd like to keep this question open since I still don't know why it isn't handled by ActionBarACtivity internally as it is done in Activity
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
Related
I have looked at the solution below, and it looks strange to me:
https://stackoverflow.com/a/13074955/499206
Is it really necessary to explicitly remove the fragment using a fragment transaction if I pop it from the backstack using PopBackStackImmediate()?
Shouldn't the framework handle it for me?
At least I don't see the fragment in the fragment manager anymore after PopBackStackImmediate().
Actual problem / TL;DR: Except when using one way I consider a hack, I can't pop the activity's entire backstack to return to my initial state with a single fragment added during onCreate(). The Fragment is present in the Activity State Manager dump, but is not visible, leaving me with an empty screen.
I have encountered this issue today and am mostly trying to understand whether this is caused by a bug or by my misunderstanding of the FragmentManager's BackStack. I want to make sure I'm not misusing Fragments or building on shaky API foundations.
I have an activity which essentially offers a descending "tree" navigation as a grid of buttons where each button opens either a sub-grid (e.g. sub-category with more buttons) or some custom form. The content of the forms is irrelevant, but users are expected to repetitively fill them in various orders.
I use Fragments from the support library (support-v4:25.1.1) and whenever a new "screen" is required (either for a form or a sub-grid) it will be added using a transaction on the activity's FragmentManager similar to:
/* adding a new screen, going further down our nav tree */
fragmentManager
.beginTransaction()
.addToBackStack("...")
.replace(R.id.container, newFragment)
.commit();
Every transaction looks like that except the setup of the initial state of my activity, which adds the initial "root grid" fragment without adding the transaction to the backstate.
/* adding the initial fragment during the first execution of activity's onCreate(). */
fragmentManager
.beginTransaction()
.replace(R.id.container, rootFragment)
.commit();
The reason I'm posting here is that I encountered a very strange behaviour when attempting to pop the entire BackStack, which is supposed to bring the user back to the initial state of the activity which has the root grid (the one added in the code above). Almost every technique I tried effectively clears the backstack, but leaves an empty screen instead of my initial main-menu grid. After looking around on multiple questions here and the docs, I tried multiple solutions and only this hack-looking one has worked:
int remainingEntries = fragmentManager.getBackStackEntryCount();
while (remainingEntries-- > 0) {
fragmentManager.popBackStackImmediate();
}
I say this seems like a hack because it requires me to immediately (synchronously) pop an arbitrary number of backstack entries to reach the root, whereas from my understanding it should be possible to do that with a single asynchronous method call:
fragmentManager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
or as I've also seen posted around here:
int id = fragmentManager.getBackStackEntryAt(0).getId();
fragmentManager.popBackStack(id, FragmentManager.POP_BACK_STACK_INCLUSIVE);
This is only an issue with the root fragment, probably because the transaction which adds it to the view is not added to the backstack, since that would cause an extra empty screen to be shown when the user presses the back button to close the activity.
More details:
According to the Activity State Manager dump my fragment still has its view, but has both mFragmentId and mContainerId set to #0.
I do add another fragment I haven't discussed here which retains its instance, but it is only used to hold some data and has no view whatsoever.
There is a PopBackStackImmediate bug in support library version 25.1.0 and above.
Since this issue has been reported either you have to wait for solution, Or you can downgrade the support library version to 25.0.1 and get the expected behavior.
UPDATE
Looks like this bug has been resolved in support library version 25.3.1. Update your library to version 25.3.1.
After upgrade from Android support library 23.1.1 to the latest 25.1.0 the behavior from popBackStack() has changed in our application.
For example I have three FragmentTransactions on BackStack. Now the fourth FragmentTrsansaction is added to BackStack. FragmentTransactions are like this:
FragmentTransaction transaction = fragmentManager.beginTransaction()
.replace(R.id.fragment_container_single, target, CoreActivity.FRAGMENT_TAG_LEFT)
.addToBackStack(<CONCRETE_TRANSACTION_NAME>)
.commit();
Fragment#4 has logic where it routes to Fragment#5 during onViewCreated() automatically (I know, it’s ugly and I have to change this). Anyway, when I want to leave Fragment#5 I want to resume to the Fragment that has been shown prior to Fragment#4. I do this by transaction name:
getFragmentManager().popBackStack(“<CONCRETE_TRANSACTION_NAME_4>”, FragmentManager.POP_BACK_STACK_INCLUSIVE);
or this:
getFragmentManager().popBackStack(“<CONCRETE_TRANSACTION_NAME_3>”, 0);
But with this call the onCreateView() and onViewCreated() of Fragment#4 will be called and Fragment#5 will be added immediately to the BackStack. Even if I resume to Fragment#1 onViewCreated() is called for every Fragment on the BackStack – for Fragment#2, Fragment#3 and Fragment#4, even if they don’t matter for the FragmentTransaction I want to return to.
Under support library 23.1.1 I successfully jump back to Fragment#3. Fragment#4 is popped inclusive without calling onViewCreated(). This seemed more intuitive and right to me, because calling onViewCreated() for Fragments "outside" of my FragmentTransaction seems unnecessary?
I know, addToBackStack() only saves the transactional states and not the Fragments itself and Fragments maybe have to be recreated.
There are similar posts to that, but I want to understand why it breaks after support library update. I can’t find the change anywhere. Was this a Bug or is it a Bug now?
I'am going on to change my code.
Thanks for help!
UPDATE
I've got more lifecycle issues/changes after upgrading to support-library 25.1.0. There is an Google issue where I commented my issues additionally. Currently I had to go back to the latest 24.2.1 and keep an eye on this topic. This is just my temporary solution. These issues start with support-library 25.0.0.
Pay attention to the new support 25.1.0.
Something changed in the fragment lifecycle.
Now there is new functionality to optimize the operations and postpone fragment transitions.
Read this:
https://code.google.com/p/android/issues/detail?id=230415
Had similar issue. Fixed in 25.3.0
Ok I am confused to hell right now.
I am running an Activity that extends FragmentActivity that implement Google maps v2.
So the map fragment is in the activty and is always running.
I now want to add a new fragment on top which I tried with getSupportFragmentManager() as well as getFragmentManager() and in both cases fragmentManager.getBackStackEntryCount() is 0 after transaction.commit().
What this causes is when I open the new fragment on top of the map I cannot go back to map but just exit the app.
I know the both managers have different backstacks but neither of them actually showed any backstack ater commit.
Can someone explane please how should I approach this problem. Android complexities run deep :)
I faced same issue.Finally have to changed extends FragmentActivity to Activity.
Now its working fine.
Instead of getSupportFragmentManager, which returns the correct number:
#Override public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0 ) {
getSupportFragmentManager().popBackStack();
}
}
You have to call a fm.executePendingTransactions() after ft.commit() or before fm.getBackStackEntryCount(). Because the commit() only schedules the transactions for a later pass.
I have a typical application. An activity which has a FrameLayout and in this layout I want to switch between fragments. This is typically and easily done with:
getFragmentManager().beginTransaction()
.replace(R.id.ac_container, new FrOverview())
.addToBackStack(null)
.commit();
The problem is, that even if I use .addToBackStack(null) (And I know it's been added 'cause the stack count increases) when I press back I exit the application. I have been trying a lot of different code stuff and checked most threads here on Stackoverflow but I can't get it to work with code (method calls etc.).
But! I can get it to work, by changing the extended class of my activity class. If my class extends Activity, it works fine. But if I use AppCompatActivity (which in turn extends FragmentActivity) then it has the bad behaviour as explained earlier.
Feels like this has to be an error on Androids part, I am not doing anything wrong to my knowledge.
Does anyone have any suggestions on how to solve this? i.e. get the back functionality and keep the ActionBar!
AppCompatActivity uses the SupportFragmentManager, you need
to switch to SupportFragment and SupportFragmentManager