The scenario is this...I click on a button that loads the next fragment. While the animation is still going I hit the recent apps button. After the recent apps screen is shown i return to my app.
Then I see the two fragments drawn on top of each other. As if Android forgot to remove the previous fragment.
I click back to remove all fragments until I reach the first fragment loaded in this FragmentActivity. I log all the fragments I get from FragmentManager and it only shows one. But I still see two fragments drawn one on top of the other. The one that should have been removed is not responding to touch events and the other one responds as it should.
Is this an Android bug or my fault? Is there any way to fix this or prevent it from happening ?
It does not happen every time. This is how I load my fragments
public void loadNewFragment(AnimFragment newFragment, boolean addToSTack, boolean animate, String tag) {
if (newFragment != null) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setCustomAnimations(R.anim.fragment_enter_from_right, R.anim.fragment_exit_to_left, R.anim.fragment_enter_from_left, R.anim.fragment_exit_to_right);
transaction.replace(R.id.fragment_container, newFragment, tag);
if (addToSTack) {
transaction.addToBackStack(tag);
}
int transactionId = transaction.commit();
newFragment.setTransactionId(transactionId);
}
}
and I remove them on back pressed with
getSupportFragmentManager().popBackStackImmediate();
In my opinion "below" fragment is onPause() state and it's still alive and visible.
Try to use white background on the root layout of the front fragment to avoid showing the below fragment. Moreover the root layout size (width and height) of the front fragment should be match_parent.
Check this site to understand Fragment lifecycle.
https://google-developer-training.gitbooks.io/android-developer-advanced-course-concepts/unit-1-expand-the-user-experience/lesson-1-fragments/1-2-c-fragment-lifecycle-and-communications/1-2-c-fragment-lifecycle-and-communications.html
Related
I'm working on a application which has Single activity rest all fragments...
This issue i'm facing occurs occasionally.. like when ever i press back button from any of the fragment the previous fragment appears... but sometimes it happens that My activity shows blank screen with action bar present, and rest of the screen is blank instead of showing the fragment... Since this issue appears occasionally i'm not able to debug it also... Any idea why it happens so??
Here is the code fragment transaction..
private void loadFragment(Fragment fragment, int activityNumber) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction trnx = manager.beginTransaction();
trnx.replace(R.id.fragPage, fragment, "Current_Fragment");
if (activityNumber != FragmentActivityNumbers.HOME_ORDERNUM) {
trnx.addToBackStack(null);
}
trnx.commit();
}
I suggest to test if its the first fragment so you use Add instead of replace then you can use replace for the others fragments .
When I start my app it runs an AsyncTask to load up and then in onPostExecute, I then setContentView to the new layout then add a fragment with two buttons offering two modes by an add FragmentTransaction. After one of the two modes is clicked, it then replaces the fragment with yet another FragmentTransaction using the replace method.
If the app crashes it returns to the first screen, loading up the two buttons offering the two modes. In this case if either mode is selected, the second fragment is loaded but is now the background is suddenly transparent showing the two buttons below and they remain clickable. If they are clicked again they properly replace the fragment so that it isn't visible below. This is just weird, I can't understand what could cause this.
I've researched and seen these two similar questions, one and two, which suggested that it might be because the ID is wrong or I have defined the fragment in XML. Neither of these two factors are the case.
My code is shown below:
Below I replace the loading screen.
#Override
protected void onPostExecute(Void result) {
setContentView(R.layout.activity_main_screen);
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.add(R.id.fragment_container, new ModeFragment())
.commit();
}
After which, when a button is clicked I pass the fragment I wish to replace the current with into this method below:
private void replaceCurrentFragment(Fragment fragment) {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.fragment_container, fragment)
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)
.addToBackStack(null).commit();
}
This works the first time, however if a crash occurs then the app returns to the first fragment and the second time this method is passed, the new replacing fragment is semi-invisible. Clicking the button on the first fragment again calls this method again and it is now fine.
Obviously I don't want the app to crash so this shouldn't occur, but I get this feeling that there's something wrong with how I'm writing my code.
I've had the same problem happen to me, and it was because I loaded a fragment in the OnCreate of my Activity, without checking if there was a savedInstanceState, so android first reopen all old fragments, then do the OnCreate, which added the fragment over the old ones without replacing them so when you navigate to another fragment, it only replaces the top one, but not the bottom one, so you will see the fragments under it.
Might not be exactly the same thing for you, but it might help you figure it out.
Since I had to do a custom menu for my application, I've made a fragment which act like an actionbar at the bottom of my app!
But I have some conflicts with the stack (when pressing the back button).
How should I program the following method?
public void changerContenu(int frameLayout, Fragment frag, boolean ajouterAuStack)
{
if(R.id.frameContent != frag.getId())
// * if the current displaying
// fragment in the layout is the same as the one that I received in param#2 (frag),
// then don't do anything.
{
ft = fragmentManager.beginTransaction();
ft.replace(frameLayout, frag);
if(ajouterAuStack)
ft.addToBackStack(null);
ft.commit();
}
}
I know my "if" doesn't make any sense right now, trying to figure out how to come to the desired result :( Right now, when I visit the fragment once (and it's pushed into the stack) I cannot revisit it using my button in my "home made actionbar" until I hit the back button a few times to clean the stack.
I only want to not duplicate items in my stack if I click few times the same button on my menu...
Thanks guys!
I want to understand the question better.
You have a fragment that acts like an action bar. If the fragment is already displaying, you don't want to do anything; otherwise, you want to display it. In what circumstances do you want to go Back, and what should happen?
As a note, a single actionbar at the bottom doesn't fit in with the typical Android design. Users may not understand how to use your app. I'm interested in knowing why the regular ActionBar doesn't work for you.
For future reference, I got it working that way:
public void changerContenu(int frameLayout, Fragment frag, boolean ajouterAuStack, String tag)
{
ft = fragmentManager.beginTransaction();
ft.replace(frameLayout, frag);
if(ajouterAuStack && (fragmentManager.getBackStackEntryCount() == 0
|| fragmentManager.getBackStackEntryAt(fragmentManager.getBackStackEntryCount()-1).getName() != tag))
ft.addToBackStack(tag);
ft.commit();
}
Scenario what i'm trying to achieve:
Loading activity with two frame containers (for list of items and for details).
At the app launch time add listFragment in listFrame and some initial infoFragment in detailsFrame containers.
Navigating through list items without adding each detail transaction to back stack (want to keep only infoFragment in stack).
As soon as user hit back button (navigate back) he falls back to intial infoFragment what was added in launch time.
If sequential back navigation fallows then apps exit.
My code:
protected override void OnCreate(Bundle savedInstanceState)
{
...
var listFrag = new ListFragment();
var infoFrag = new InfoFragment();
var trans = FragmentManager.BeginTransaction();
trans.Add(Resource.Id.listFrame, listFrag);
trans.Add(Resource.Id.detailsFrame, infoFrag);
trans.Commit();
...
}
public void OnItemSelected(int id)
{
var detailsFrag = DetailFragment.NewInstance(id);
var trans = FragmentManager.BeginTransaction();
trans.Replace(Resource.Id.detailsFrame, detailsFrag);
if (FragmentManager.BackStackEntryCount == 0)
{
trans.AddToBackStack(null);
}
trans.Commit();
}
My problem:
After back button has been hit, infoFrag is overlapped with previous detailFrag! Why?
You can do this:
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack(getSupportFragmentManager().getBackStackEntryAt(0).getId(), getSupportFragmentManager().POP_BACK_STACK_INCLUSIVE);
} else {
super.onBackPressed();}
In your activity, so you to keep first fragment.
You shouldn't have, in your first fragment, the addToBackStack. But, in the rest, yes.
Very nice explanation by Budius. I read his advice and implemented similar navigation, which I would like to share with others.
Instead of replacing fragments like this:
Transaction.remove(detail1).add(detail2)
Transaction.remove(detail2).add(detail3)
Transaction.remove(detail3).add(detail4)
I added a fragment container layout in the activity layout file. It can be either LinearLayout, RelativeLayot or FrameLayout etc.. So in the activity on create I had this:
transaction.replace(R.id.HomeInputFragment, mainHomeFragment).commit();
mainHomeFragment is the fragment I want to get back to when pressing the back button, like infoFrag. Then, before EVERY NEXT transaction I put:
fragmentManager.popBackStackImmediate();
transaction.replace(R.id.HomeInputFragment, frag2).addToBackStack(null).commit();
or
fragmentManager.popBackStackImmediate();
transaction.replace(R.id.HomeInputFragment, frag3).addToBackStack(null).commit();
That way you don't have to keep track of which fragment is currenty showing.
The problem is that the transaction that you're backing from have two steps:
remove infoFrag
add detailsFrag (that is the first1 detail container that was added)
(we know that because the documentation This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here. )
So whenever the system is reverting that one transaction is reverting exactly those 2 steps, and it say nothing about the last detailFrag that was added to it, so it doesn't do anything with it.
There're two possible work arounds I can think on your case:
Keep a reference on your activity to the last detailsFrag used and use the BackStackChange listener to whenever the value change from 1 to 0 (you'll have to keep track of previous values) you also remove that one remaining fragment
on every click listener you'll have to popBackStackImmediatly() (to remove the previous transaction) and addToBackStack() on all transactions. On this workaround you can also use some setCustomAnimation magic to make sure it all looks nice on the screen (e.g. use a alpha animation from 0 to 0 duration 1 to avoid previous fragment appearing and disappearing again.
ps. I agree that the fragment manager/transaction should be a bit more clever to the way it handles back stack on .replace() actions, but that's the way it does it.
edit:
what is happening is like this (I'm adding numbers to the details to make it more clear).
Remember that .replace() = .remove().add()
Transaction.remove(info).add(detail1).addToBackStack(null) // 1st time
Transaction.remove(detail1).add(detail2) // 2nd time
Transaction.remove(detail2).add(detail3) // 3rd time
Transaction.remove(detail3).add(detail4) // 4th time
so now we have detail4 on the layout:
< Press back button >
System pops the back stack and find the following back entry to be reversed
remove(info).add(detail1);
so the system makes that transaction backward.
tries to remove detail1 (is not there, so it ignores)
re-add(info) // OVERLAP !!!
so the problem is that the system doesn't realise that there's a detail4 and that the transaction was .replace() that it was supposed to replace whatever is in there.
You could just override onBackPressed and commit a transaction to the initial fragment.
I'm guessing but:
You've added the transaction to replace infoFrag with 1st detailsFrag into the backstack.
But then you replace 1st detailsFrag with 2nd detailsFrag.
At this point when you click back, the fragment manager cannot cleanly replace 1st detailsFrag with infoFrag as 1st detailsFrag has already been removed and replaced.
Whether the overlapping behaviour is expected or not I don't know.
I would suggest debugging the Android core code to see what it is doing.
I'm not sure whether you can achieve without say overriding Activity::onBackPressed() and doing the pops yourself having added all transactions to the backstack.
In my application i have one activity and i am adding two fragments at run time.I need to swap these two fragment simultaneously. Fragment 1 consist a button and i want when i click that button fragment 1 moves to right side of the screen and other fragment to the left side of the activity.
In the onClick method of the button i tried something like this
#Override
public void onClick(View v) {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment newFragment = getFragmentManager().findFragmentById(R.id.pageA);
ft.remove(newFragment);
Fragment newFragmentB = getFragmentManager().findFragmentById(R.id.pageB);
ft.remove(newFragmentB);
ft.add(R.id.pageB, newFragment);
ft.add(R.id.pageA, newFragmentB);
ft.addToBackStack(null);
ft.commit();
}
But i am getting the following error
java.lang.IllegalStateException: Can't change container ID of fragment PageA{40653da0 #0 id=0x7f060001}: was 2131099649 now 2131099650
I want something like this when i click the button on Page A then Position of Page A and PageB should swap with each other.
I have a similar issue ( IllegalStateException: Can't change container ID of Fragment ) and i solved by swapping the containers instead of the fragments... Nonetheless i still have no clue as to whether it's possibile to swap directly fragments. As I wrote in the aforementioned post, it seems to work only on ics!
I posted a solution to this problem to a similar question. My approach is to re-create the fragment, but keeping the state by saving it first and re-applying it to the new instance.
See https://stackoverflow.com/a/14951987/599614.