"Can't change container ID of fragment" when using "replace" - android

So I've found a few similar cases, but nothing about this specific case.
I have a FrameLayout which I gave the id "container", and contains a few different fragments.
In my code for the activity that contains that FrameLayout, I'm trying to switch between the fragments with a function that receives a fragment.
In that code:
a. I have defined private FragmentManager fm = getSupportFragmentManager();
b. I have defined FragmentTransaction ft; to use later.
c. My function is:
private void setActiveFragment(Fragment fragment){
//Determine which button should be marked as "active"
determineButtonByFragment(fragment);
//Repalce fragment
ft = fm.beginTransaction();
ft.replace(R.id.container, fragment);
ft.addToBackStack(String.valueOf(fragment.getId()));
ft.commit();
}
Any idea why i would get this error on the "replace"?
EDIT:
Ok, I just realized a bit more about fragments. Since all 5 fragments are added to the FrameLayout, the "replace" won't work. You get the "can't chance container ID error" when you're trying to move a fragment from 1 parent view to another (Or in this, case, to the same one), without detaching it first.
Let's say I have fragments A, B, C, D, E.
If I want to replace to fragment B right now, I won't be able to do it until I remove it from it's original parent (Or at least that's how I think it works. please enlighten me otherwise). The only question that remains now, is how do I switch between my fragments correctly...

Alright, apparently you cannot use the "replace" method on fragments which are hard-coded in the layout:
Replacing a fragment with another fragment inside activity group
What I have to do now, is inside the function, to find a way to determine the fragment I want to display, create a new instance of it, and use "replace" on it.

Related

Fragments lifecycle

I'm learning about fragments I have some doubts. Consider following code:
FragmentManager fm = getFragmentManager();
Fragment MyFragment = new Fragment();
fm.beginTransaction().replace(R.id.my_container, MyFragment).addToBackStack(null).commit();
My question is:
what exactly does replace do?
What happens if I create many fragments this way (to replace previous ones in a container).
Can it in any way be bad for memory usage?
Is it considerably better just to change fragment's content?
Replace removes all the fragments that are in the container and adds the new fragment to the container. (if there isn't a fragment in the container then it just adds the new one).
If you create many fragments this way then every transaction is saved to the backstack so you can reverse the transaction by pressing the back button.
The only thing you can do is to create a variable fragmentTransaction and use the fm.beginTransaction() only once and not every time you want to replace the fragment in the container.
I don't think so, fragments should be modular and reusable.
You can read more here:
https://developer.android.com/guide/components/fragments.html
it simple put another "layer" on container.
appcrash
yes
No, fragment is the easiest way.
Using fragment & backstack tag to reference to a Fragment if you want to call fragment again and process Back button.
fm.beginTransaction().replace(R.id.my_container, MyFragment, "FRAGMENT_TAG").addToBackStack("FRAGMENT_BACKSTACK_TAG").commit();

Replaced fragment still visible

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.

Activity destroyed by garbage collection and all fragments in backstack are showing instead of only current activity

So I have enabled the setting to destroy actvities when you navigate away from an activity
Settings=>Developer Options=>Don't Keep activites
This should basically replicate an activity or fragment getting garbaged collected and then I have to restore the data via the bundle savedinstancestate.
So I understand how that works. But it seems when I navigate from fragment 1 to fragment 2 and then put the application in the background and then in the foreground(destroying the activity)
Both fragment 1 and fragment 2 show at the same time. In which only fragment 2 should be showing.
I do not know if this is something standard that I have to manage hiding and showing fragments onsavedinstance. Or if something in my code is breaking things. Below is how I push fragments which I hope is helpful:
public void pushFragmentWithAnimation(FragmentManager fm, int parentId, Fragment currentFrag, Fragment newFrag, int animEntry, int animExit) {
hideSoftKeyboard(currentFrag.getActivity());
FragmentTransaction ft = fm.beginTransaction();
// See: http://developer.android.com/reference/android/app/FragmentTransaction.html#setCustomAnimations(int, int, int, int)
ft.setCustomAnimations(animEntry, animExit, animEntry, animExit);
ft.add(parentId, newFrag, String.format("Entry%d", fm.getBackStackEntryCount())).hide(currentFrag).show(newFrag);
ft.addToBackStack(null);
ft.commit();
}
Fragment 1 is still in the backstack because when I press back I only see fragment 1. Let me know if you know why this is happening.
The lifecycle of XML added Fragments and programmatically added Fragments differ enough to make mixing them a bad idea, as explained in detail here.
The easiest way around this is to make all fragments programmatically added by replacing your XML inflated Fragment with a FrameLayout of the same ID, then in your onCreate add
FragmentManager fragMgr = getSupportFragmentManager();
if (null == fragMgr.findFragmentByTag(FRAG_TAG))
{
fragMgr.beginTransaction().
add(R.id.fragment, new Fragment1(), FRAG_TAG).commit();
}
Where FRAG_TAG is any unique string. This ensures that Fragment1 is only created if it is not already in the layout.
I am not entirely sure why this solution works. I assume its related to if the activity gets killed that it does not keep track of which fragment is currently shown and shows all of the fragments. So basically I needed to replace:
ft.add(parentId, newFrag, String.format("Entry%d", fm.getBackStackEntryCount())).hide(currentFrag).show(newFrag);
with
ft.replace(parentId, newFrag, tag);
Then when I create the initial fragment in the main activity. I only would do that when
if(savedInstanceState==null){
My updated code is below: https://github.com/CorradoDev/FragmentTest/tree/2c53f9f42e835da768f61b0233f3ab5b3adf2448

Android Fragment Issues

I am using Fragments to represents different views in my application. I replace the fragments using the following code when navigating between views:
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
ft.replace(R.id.main_linearlayout_fragmentcont, frag);
ft.addToBackStack(null);
ft.commit();
I have run into a number of problems when rotating and the activity is reconstructed. I need to support old versions of android so android:configChanges="orientation" isn't an option. A lot of the issues are due to the nature of how Android saves Fragment state.
These are the problems I am running into:
1) The Fragment transitions don't remember my custom animations for pop events when they are restored automatically after a rotate. They do however remember my BackStack. I know I can write my own back handler that does a replace using animations and get rid of pop all together but I was wondering if there is a way to either reset the animation before calling popBackStack() or a way to have the FragmentManager remember the animations when it auto restores after rotate.
2) The other issue I have is that I have a bunch of child views (linearlayouts) in one of my top level fragment views that contain their own fragments. These child views are created and populated programmatically. When my fragment is recreated after rotation, I programmatically reconstruct the child views in onCreateView of the Fragment Object and I end up with duplicate fragments under each of the child views (1 - I create programmatically and 1 - Android Fragments create from restore). I am assuming this is because I programmatically reconstruct the child views after rotation with the same id. Is there a way to prevent Fragments from being restored? When does Android inject the Fragments from savedState into these views I construct programmatically? How would I prevent this from happening?
3) The above replace code seems to fire onCreateView multiple times for my frag (Fragment) object. This is without rotation and happens when I run the above code only once. Is there a reason that onCreateView of a Fragment would be called multiple times with the above code?
Questions about Fragments:
1) Can I prevent Android from auto restoring fragments when an activity is reconstructed? How would I go about this? Is it based on the ID of the LinearLayout? Could I call removeAllViews of the LinearLayout containing the fragment onStop? That way the view doesn't exist when it saves?
2) Is there a way to add a Fragment to a LinearLayout that I have a reference to but that doesn't have an ID? It appears the Fragment add, replace APIs require an int ID.
Thanks!
1) if you find out how let me know, I'm also pissed off by that
2) you're probably calling add on the FragmentTransaction inside the top level fragment, but the restore operation is also adding, so duplicates! option 1. Use replace instead. option 2. (preferred) Check if(savedInstances==null) { // do transaction } else { //let the system rebuilt it itself}
3) If you're changing the layout (by calling add or replace) of a view that is a part of a fragment, the manager call the method to creates the view again. I'm still not sure if that is a bug or a feature, and if it's a feature why it is. If you find out let me know
1) (supposed to be 4, no?) don't mess with the layouts, if u want to remove, remove them using while(popBackStackImmediatly){}, but if you go deeper and understand what the system is doing, usually there's no reason to not let it do it automatically.
2) (supposed to be 5, no?) if you have a reference you have the id View.getId()
happy coding!
If you are change the orientation of device then check the validation in activity and it also manage the fragment with stack so your flow not damage in that case.
if(savedInstanceState == null) {
mFragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction =
mFragmentManager.beginTransaction();
FragmentOne fragment = new FragmentOne();
fragmentTransaction.add(R.id.fragment_container, fragment);
fragmentTransaction.commit();
}

Swap two fragment simultaneously

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.

Categories

Resources