So, I'm developing an app with fragments, and everything is fine, except that when I change orientation the entries on my ListFragment duplicate like this:
And when I go back to portrait mode, the items are still duplicated; until I kill the process and restart.
To populate the list I'm using an AsyncTaskLoader, but it only runs once, when the application starts for the first time; then my adapter gets filled with the info for the rows.
I'm not using the <fragment> tag in my main.xml, the fragment is added by code in the 'parent' activity like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
if (savedInstanceState == null) {
TodoListFragment fragment = new TodoListFragment();
FragmentTransaction trans = getFragmentManager().beginTransaction();
trans.replace(R.id.fragment_list_container, fragment,
LIST_FRAGMENT_TAG);
trans.commit();
}
}
The replace was and add, but changed trying to test if it made any difference, it didn't.
I also tried using the setRetainInstance(true) method, to no avail.
I'm stumped, any help would be greatly appreciated.
You say the AsyncTaskLoader only runs when the app starts... On orientation change the app is restarting, so maybe it's running a second time?
Related
I working on a app whitch has 1 activity with 2 buttons (Map and Listview). By clicking on the buttons the fragment into the FrameLayout needs to change from a listview to a mapview (or from map to listview).
I got this working and the fragments are show the data exacly as i want BUT...
I have a few problems by switching between the fragments. I struggled with the code and the best i got working is this:
FragmentManager fragmentManager;
Fragment listOverview;
AlertOverviewMapsFragment mapsOverview;
#Bind(R.id.fr_overviewContainer) FrameLayout fr_overviewContainer;
#OnClick(R.id.btn_list_overview) void openListOverview()
{
// Add the fragment to the 'fragment_container' FrameLayout
if (listOverview == null) { listOverview = new AlertOverviewListFragment();}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fr_overviewContainer.removeAllViews();
fragmentTransaction.add(R.id.fr_overviewContainer, listOverview).commit();
}
#OnClick(R.id.btn_maps_overview) void openMapsOverview()
{
// Add the fragment to the 'fragment_container' FrameLayout
if (mapsOverview == null){ mapsOverview = new AlertOverviewMapsFragment();}
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fr_overviewContainer.removeAllViews();
fragmentTransaction.add(R.id.fr_overviewContainer, mapsOverview).commit();
}
The problem is when i click a second time on a button the app crashes and i get a java.lang.IllegalStateException: Fragment already added error. I understand that it means that i added the fragment to the manager (or transaction, dunno). I can create or find some workarounds for this but since this is nothing new i'am looking for the usual way to switch (replace) between the 2 fragments.
The fragments contains data which are loaded from the internet, it will be nice when the data is saved and loaded again by reloading the fragment. (so the user sees the old data when it is updated again in the background)
A CLEAR code example would be nice.....
i read something about the fragment manager and transactions and so as i understand the manager makes it posible to add, remove, delete......... fragments from a transaction? so i need 1 transaction per activity? or is this twisted? so the manager contains the fragments? this part is not clear and realy complicated for me. I'am happy when someone can help me with a solution for my problem and it would be nice when someone can explain how the transactions and the managers work in a realy SIMPLE way becouse i'am just started with programming Android apps.
Thanks for reading my problem and lot more thanks for the people who takes the time and patience for writing an answer!
I'm having an issue of having two instances of the same fragment being attached to the activity. I have ActivityA attaching FragmentA on onCreate. When I leave the app while being on this Activity, browse other apps for a while, and return to the app, I see that the system is trying to re-create the activity. My log shows the code from the Fragment being ran TWICE. My guess is the Fragment is already attached but then the Activity attempts to create a new instance of FragmentA.
What happens to the Activity/Fragment when the system removes them from memory, and what's the best way to handle this? Any links would be helpful.
Will provide code if needed.
The best way to handle this is to check in your onCreate() method if your activity if being recreated from a previous state or not. I'm assuming you add your fragment on the onCreate() method of your activity. You can do something like this:
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (savedInstanceState == null)
{
// Add the fragment here to your activity
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.content, new YourFragment());
ft.commit();
}
}
By doing this, you are basically saying that if a previous state is not found, you add your fragment. Otherwise you automatically get back the fragment that already exists.
I'm currently dealing with an issue with Android & It's Re-Creation Cycle on screen rotation:
I have one single Activity and lots of Fragments (Support-V4) within.
For example, the Login it's on a Single Activity with a Fragment, when the logs-in then the App changes it's navigation behavior and uses multiple fragments, I did this, because passing data between Fragment A to Fragment B it's way much easier than passing data Between an Activity A to an Activity B.
So My issue it's presented when I rotate the device, on my first approach, the initial fragment was loaded, but what would happen, if the user it's on Page 15 and it rotates it's device, it would return to Fragment 1 and give a very bad user-experience. I set all my fragments to retain their instance and added this on the MainActivity on Create:
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
initBackStackManager();
initControllers();
mayDownloadData();
setTitle();
if(savedInstanceState == null){
addAreaFragment();
}
}
Now, the first fragment is not loaded after screen orientation change, but If I try to make a fragment transaction, it says Can not perform FragmentTransaction.commit() after onSaveInstanceState(), is there a way to handle this? Or Do I really really need to use multiple Activities with a Fragment embedded within?
Thank you very much!
EDITED
I forgot to add that this happens only on a specific Fragment... For example I have the following fragment flow:
AreaFragment -> WaiterSelectionFragment -> WaiterOptionsFragment.
If I'm in the AreaFragment and I rotate the device I can still add/replace fragments and nothing happens, no error it's being thrown. If I'm on the WaiterSelectionFragment no error happens too. BUT, If I'm on the WaiterOptionsFragment the error it's being thrown. The WaiterSelectionFragment has the following structure:
LinearLayout
FragmentTabHost
Inside the FragmentTabHost there are some fragments, and that's where the error it's happening. You might wonder Why FragmentTabHost? easy, the Customer wants that App to show the TabBar, If I use Native Android Tabs the Tabs get rearranged to the ActionBar when on
Landscape position.
EDIT 2
I've used the method provided by #AJ Macdonald, but no luck so far.
I have my Current Fragment being saved at onSaveInstanceState(Bundle) method and restore my fragment on onRestoreInstanceState(Bundle) method on the Android Activity, I recover my back button and the current Fragment but when I get to the third Fragment the error still occurs. I'm using a ViewPager that holds 4 Fragments, Will this be causing the Issue? Only on this section of the App Happens. I've 4 (main workflow) fragments, on the First, Second and Third Fragment no error it's being presented, only on the ViewPager part.
Give each of your fragments a unique tag.
In your activity's onSaveInstanceState, store the current fragment. (This will probably be easiest to do if you keep a variable that automatically updates every time the fragment changes.)
In your activity's onCreate or onRestoreInstanceState, pull the tag out of the saved bundle and start a new fragment of that type.
public static final int FRAGMENT_A = 0;
public static final int FRAGMENT_B = 1;
private int currentFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//other stuff
if(savedInstanceState == null){
addAreaFragment();
currentFragment = FRAGMENT_A;
}else{
currentFragment = savedInstanceState.getInt("currentFragment");
switch(currentFragment){
case FRAGMENT_A:
addAreaFragment();
break;
case FRAGMENT_B:
addFragmentB();
}
}
}
// when you switch fragment A for fragment B:
currentFragment = FRAGMENT_B;
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putInt("currentFragment", currentFragment);
super.onSaveInstanceState(savedInstanceState);
}
A suggestion to try is to use FragmentTransaction.commitAllowingStateLoss() in place of FragmentTransaction.commit(). That should stop the Exception from being thrown, but the downside is if you rotate the device again the most recent state of the UI may not return. That is a suggestion given that I am not sure of the effect of using FragmentTabHost, if it has any effect at all.
I have a problem with a fragmented layout and I sincerely apologize if it has been answered before and I was too dumb to find it. I searched for hours and got nothing (well, I got lots but nothing solved my problem).
So here's my setup: I have a two pane layout using two FrameLayouts as containers for my fragments. activity_listing.xml:
<FrameLayout android:id="#+id/listing" />
<FrameLayout android:id="#+id/details" />
On opening the app (onCreate in the fragment's activity) a fragment called Listing is added to FrameLayout "listing" programmatically using a FragmentTransaction.
public class ListingActivity extends FragmentActivity
implements ListingFragment.Callbacks {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_listing);
// Displaying the first listing here ...
Fragment fragment = new ListingFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.listing, fragment);
ft.addToBackStack(null);
ft.commit();
...
}
This ListingFragment is replaced a few times during runtime, so I have to create it instead of defining it in XML. I tried the latter with
<fragment android:name="com.example.app.ListingFragment" />
but then it won't be replaced or removed programmatically later on. I can only add() and then the old one is visible through the new one. But that's not the problem here - just an explanation for my way.
So far all of this works as it should, Listing is created and displayed etc., so no problems there. But:
When ListingFragment is displayed at startup and I press the back key, at the last position FrameLayout "listing" is emptied instead of dropping back to the Homescreen! I figured it has to be because onCreate of my ListingActivity I display an empty frame and add() a ListingFragment to it. So the back stack has the empty frame in it, too. Right?
I tried solving the situation by this to ListingActivity:
#Override
public void onBackPressed() {
super.onBackPressed();
if(getSupportFragmentManager().getBackStackEntryCount() == 0) {
this.finish();
}
}
But somehow that does not look or feel right ... it looks like a bad work around.
So is there any way to insert the fragment before the view with the empty FrameLayout is inflated so there is no empty state to "back" to? Or is it possible to remove the "empty" state from the back stack even though it is not in it? Any other ideas on how to avoid the empty frame after hitting "back"?
Thank you very much for your efforts in advance!
Don't call ft.addToBackStack(null) when you add the Fragment in onCreate. That tells the FragmentManger that you have another state BEFORE that fragment that you want to be able to jump back to.
I have a ListFragment where I use a custom adapter to populate the listview. All is well until I change orientation and scroll. Then it looks like this:
I am guessing it has something to do with me fumbling with a view holder, but I can't access the code right now.
The reason for the overlapping fragment was that I used FrameLayout and added the fragment with FragmentTransition.add(...). When I changed .add() to .replace() the old fragment was removed and the new one was added and my problem was solved.
I had similar problem and according to that i am telling the solution :-
you are getting this blur because every time on orientation change somewhere new instance of listfragment is created (may be it is in oncreate()), so you have to just make an instance of list fragment once and on orientation change replace that fragment rather than adding again.
Changing orientation causes onCreate to restart unless you include this method
public void onConfigurationChanged(Configuration newConfig)
{
super.onConfigurationChanged(newConfig);
}
and put this in the activity section of your manifest
android:configChanges="orientation"
Check that this is the first time onCreate() is called,
in other words, determine if the callback is not due to screen rotation.
protected void onCreate(Bundle savedInstanceState) {
if(savedInstanceState == null) {
// transition.add(Your fragment,...)
}
}
I had the same problem, and its because of FrameLayout usage,
First you have to check if your fragment has already added to the activity :
String TagName = "F";
Adding a fragment without a UI If you want to get the fragment from the activity later, you need to use findFragmentByTag(). "Google"
http://developer.android.com/guide/components/fragments.html#Adding
Fragement F = getFragmentManager().findFragmentByTag(TagName);
Then check
if(F == null) {
F = new F();
getFragmentManager()
.beginTransaction()
.add(R.id.container, F, TagName)
.commit();
}
the target here is to avoid adding or creating new instance of the fragment, that persist during the configuration change which causes the problem when using the FrameLayout as container.
Solution 2 (Simple):
The only thing you have to do here is to change your container to ex:LinearLayout , and that's it. but in my opinion this is not the best solution because of multiple instances of the fragment.
Hope this help;