can I rely on savedInstanceState != null for adding fragment to layout - android

android developer tutorial (code beloew) only adds fragment to layout when savedInstanceState != null.
That indicates when savedInstanceState == null fragment is always not added to layout, and when savedInstanceState != null fragment is always added to layout.
Why can I rely on it? I can not find a direct relationship with savedInstanceState and if a fragment is added to layout or not.
the code I am talking about:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
// *** question: I don't understand the comment above. (described in my question)
if (savedInstanceState != null) {
return;
}
HeadlinesFragment firstFragment = new HeadlinesFragment();
firstFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
}

Fragments are attached to activity automatically. You don't need to add it again if you add it ones. Android will do it by itself.
If savedInstanceState bundle is not null in onCreate then your Activity was recreated(maybe you change orientation or Android killed your application because of low memmory) and you don't need to attach fragment again.
Of course this depend on different situation. There are cases when you need to add different Fragments in onCreate every time Activity change orientation. Then you don't need to use that check.

Related

Why do we check for overlapping fragments in OnCreate? (from the documentation)

In the documentation, we have to check if we restore an activity, in order to avoid "overlapping fragments" : why?
Either the activity is destroyed and the fragment is destroyed, so we have to recreate both of them. Or the fragment is retained so that it keeps some data. But in this code, we check if we restore the activity, and if we do, we return the function to avoid "overlapping fragments" :
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.fragment_container) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;//**HERE**
}
// Create a new Fragment to be placed in the activity layout
HeadlinesFragment firstFragment = new HeadlinesFragment();
// In case this activity was started with special instructions from an
// Intent, pass the Intent's extras to the fragment as arguments
firstFragment.setArguments(getIntent().getExtras());
// Add the fragment to the 'fragment_container' FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, firstFragment).commit();
}
}
}
Activity stores information about added fragments in its fragment manager. This information persists after configuration changes. Also fragment can save their state, if you want. You can read about retained fragment, which will not be recreated after configuration changes.
Suggestion:
You used fragment manager to add your fragment to activity. After activity recreation the fragment is still tied to the recreated activity.
You added your fragment via add method, and if onCreate method will be called again (for example after rotation), you will add another fragment to your activity.
To avoid it you can use replace instead of add, but in this case replaced fragment will lose your data (which you saved in savedinstancestate for your fragment, so after rotation fragment's savedInstanceState will be null).
So better to add fragment this way:
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.news_articles);
if (savedInstanceState == null) {
HeadlinesFragment firstFragment = new HeadlinesFragment()
firstFragment.setArguments(getIntent().getExtras());
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container,firstFragment).commit();
}
}
}
Understanding activity and fragment lifecycle is very important. I recommend to read related topics on android developers website or from other sources.
http://developer.android.com/training/basics/activity-lifecycle/index.html
https://www.youtube.com/watch?v=-zr5QLH4Qy4

Getting a reference to a child Fragment after the parent Fragment has been recreated

Starting Android 4.2, Android supports nested Fragments. The doc doesn't give a lot of explanations regarding nested Fragment lifecycles but from experience, it appears their lifecycle is really similar to "regular" Fragments.
It looks like there is one big difference though: child Fragments are not restored when the parent Fragment onCreate method is called. As a consequence, it seems impossible to save/restore a reference to a particular Fragment:
Using getChildFragmentManager.findFragmentByTag(String) always returns null in parent Fragment onCreate(Bundle) because mActive is null.
Using putFragment/getFragment results in a NullPointerException because getFragment looks for the size of a null mActive ArrayList.
So, my question is quite simple. Is there a correct way to retrieve a reference to a child Fragment in the parent Fragment onCreate method?
I don't think you can in onCreate as the view isn't constructed at that time. You can in onViewCreated() though. The logic I used is:
Check if there is saved state in onViewCreated(), if there is, try to get the child fragment
Then check if the child fragment is null, if it is, add it using the child fragment manager.
By "checking" I mean looking up the fragment by id. I guess by tag should work too.
AFAIK you can't get a child fragment before the view hierarchy is restored or created, but you could do the same at later time, for example in onActivityCreated()
What about setRetainInstanceState(true) on your fragment?
Could it solve your problem? It solved some problems when I have ChildFragments in a Fragment. I only have to keep a reference to the childfragment in the fragment.
But I allways did that in onCreateView(). Not sure if it will work in onCreate()
Or do you mean something completely different?
are u using FragmentPagerAdapter?
if not try FragmentPagerAdapter instead of FragmentStatePagerAdapter
I realised that have some bug when using FragmentStatePagerAdapter when i have 4 level nest.
Sorry my english is poor.
#Override
public void onCreate(Bundle savedInstanceState) {
if (savedInstanceState != null) {
mFragment1 = getFragmentManager().getFragment(savedInstanceState, STATE_Fragment1);
mFragment2 = getFragmentManager().getFragment(savedInstanceState, STATE_Fragment2);
mFragment3 = getFragmentManager().getFragment(savedInstanceState, STATE_Fragment3);
} else {
mFragment1 = SomeFragment.newInstance("param1");
mFragment2 = SomeFragment.newInstance("param2");
mFragment3 = SomeFragment.newInstance("param3");
}
super.onCreate(savedInstanceState);
mMyPagerAdapter = new MyPagerAdapter(getChildFragmentManager(), mFragment1, mFragment2, mFragment3);
}
#Override
public void onSaveInstanceState(Bundle outState) {
if (mFragment1 != null) {
getFragmentManager().putFragment(outState, STATE_Fragment1,
mFragment1);
}
if (mFragment2 != null) {
getFragmentManager().putFragment(outState, STATE_Fragment2,
mFragment2);
}
if (mFragment3 != null) {
getFragmentManager().putFragment(outState, STATE_Fragment3,
mFragment3);
}
super.onSaveInstanceState(outState);
}

Handle Fragment duplication on Screen Rotate (with sample code)

There are some similar answers, but not to this situation.
My situation is simple.
I have an Activity with two different layouts, one in Portrait, another in Landscape.
In Portrait, i use <FrameLayout> and add Fragment into it dynamically.
In Landscape, i use <fragment> so the Fragment is static. (in fact this doesn't matter)
It first starts in Portrait, then i added the Fragment by simply:
ListFrag listFrag = new ListFrag();
getSupportFragmentManager().beginTransaction()
.replace(R.id.FragmentContainer, listFrag).commit();
where ListFrag extends ListFragment.
Then i do a screen rotate. I found the listFrag is re-creating in the Landscape mode.
(In which i noticed the onCreate() method is called again with a non-null Bundle)
i tried to use setRetainInstance(false) like #NPike said in this post. But the getRetainInstance() is already false by default. It does not do what i expected as the docs said. Could anyone please explain?
The fragment i am dealing with, is a ListFragment, which does setListAdapter() in onCreate(). So the if (container == null) return null; method cannot be used here. (or i dont know how to apply).
I got some hints from this post. Should i use if (bundle != null) setListAdapter(null); else setListAdapter(new ...); in my ListFragment? But is there a nicer way to indeed removing/deleting the fragment when it is destroyed/detached, rather than doing it in its creation time? (so as the if (container == null) return null; method)
Edit:
The only neat way i found is doing getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit(); in onSaveInstanceState(). But it will raise another problems.
When screen is partially obscured, like WhatsApp or TXT dialogue boxes pop up, the fragment will be disappeared also. (this is relatively minor, just visual issue)
When screen rotate, the Activity is completely destroyed and re-created. So i can re-add the fragment in onCreate(Bundle) or onRestoreInstanceState(Bundle). But in the case of (1), as well as switching Activities, neither onCreate(Bundle) nor onRestoreInstanceState(Bundle) will be called when user get back to my Activity. I have nowhere to recreate the Activity (and retrieve data from Bundle).
Sorry I didn't say it clearly that, i already have a decision making, which the getSupportFragmentManager()...replace(...).commit(); line only run in Portrait mode.
Example code
I have extracted the simple code, for better illustration of the situration :)
MainActivity.java
package com.example.fragremovetrial;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
public class MainActivity extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (findViewById(R.id.FragmentContainer) != null) {
System.out.println("-- Portrait --"); // Portrait
ListFrag listFrag = new ListFrag();
getSupportFragmentManager().beginTransaction()
.replace(R.id.FragmentContainer, listFrag).commit();
} else {
System.out.println("-- Landscape --"); // Landscape
}
}
#Override
protected void onResume() {
super.onResume();
if (findViewById(R.id.FragmentContainer) != null) {
System.out.println("getRetainInstance = " +
getSupportFragmentManager().findFragmentById(R.id.FragmentContainer).getRetainInstance());
}
}
}
layout/activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/FragmentContainer"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
layout-land/activity_main.xml (doesn't matter)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout>
ListFrag.java
package com.example.fragremovetrial;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.widget.ArrayAdapter;
public class ListFrag extends ListFragment {
private String[] MenuItems = { "Content A", "Contnet B" };
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
System.out.println("ListFrag.onCreate(): " + (savedInstanceState == null ? null : savedInstanceState));
setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, MenuItems));
}
}
Notice i got the following
Debug messages
-- Portrait --
ListFrag.onCreate(): null
getRetainInstance = false
(rotate Port -> Land)
ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray#4052dd28}]
-- Landscape --
Previously focused view reported id 16908298 during save, but can't be found during restore.
(rotate Land -> Port)
ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray#405166c8}]
-- Portrait --
ListFrag.onCreate(): null
getRetainInstance = false
(rotate Port -> Land)
ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray#4050fb40}]
-- Landscape --
Previously focused view reported id 16908298 during save, but can't be found during restore.
(rotate Land -> Port)
ListFrag.onCreate(): Bundle[{android:view_state=android.util.SparseArray#40528c60}]
-- Portrait --
ListFrag.onCreate(): null
getRetainInstance = false
where the number of Fragment created will not increase infinitely as screen keep rotating, which i cannot explain. (please help)
The correct way to handle this is to put the following in your onCreate()
if (savedInstanceState == null) {
// Do fragment transaction.
}
I finally come up with a solution to prevent unwanted Fragment re-creating upon the Activity re-creates. It is like what i mentioned in the question:
The only neat way i found is doing getSupportFragmentManager().beginTransaction().remove(getSupportFragmentManager().findFragmentById(R.id.FragmentContainer)).commit(); in onSaveInstanceState(). But it will raise another problems...
... with some improvements.
In onSaveInstanceState():
#Override
protected void onSaveInstanceState(Bundle outState) {
if (isPortrait2Landscape()) {
remove_fragments();
}
super.onSaveInstanceState(outState);
}
private boolean isPortrait2Landscape() {
return isDevicePortrait() && (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE);
}
and the isDevicePortrait() would be like:
private boolean isDevicePortrait() {
return (findViewById(R.id.A_View_Only_In_Portrait) != null);
}
*Notice that we cannot use getResources().getConfiguration().orientation to determine if the device is currently literally Portrait. It is because the Resources object is changed RIGHT AFTER the screen rotates - EVEN BEFORE onSaveInstanceState() is called!!
If we do not want to use findViewById() to test orientation (for any reasons, and it's not so neat afterall), keep a global variable private int current_orientation; and initialise it by current_orientation = getResources().getConfiguration().orientation; in onCreate(). This seems neater. But we should be aware not to change it anywhere during the Activity lifecycle.
*Be sure we remove_fragments() before super.onSaveInstanceState().
(Because in my case, i remove the Fragments from the Layout, and from the Activity. If it is after super.onSaveInstanceState(), the Layout will already be saved into the Bundle. Then the Fragments will also be re-created after the Activity re-creates. ###)
### I have proved this phenomenon. But the reason of What to determine a Fragment restore upon Activity re-create? is just by my guess. If you have any ideas about it, please answer my another question.
In your onCreate() method you should only create your ListFrag if you are in portrait mode. You can do that by checking if the FrameLayout view that you only have in the portrait layout is not null.
if (findViewById(R.id.yourFrameLayout) != null) {
// you are in portrait mode
ListFrag listFrag = new ListFrag();
getSupportFragmentManager().beginTransaction()
.replace(R.id.FragmentContainer, listFrag).commit();
} else {
// you are in landscape mode
// get your Fragment from the xml
}
In the end if you switch from portrait to landscape layout, you don't want to have another ListFrag created, but use the Fragment you have specified in your xml.
#midnite ,first of all thanks for your question, I had the same question, on wich I worked for days. Now I wound some tricky resolution to this problem - and want to share this to community.
But If someone has better solution, please write in comments.
So, I have same situation - different layouts for two device orientation. In portrait there is no need for DetailsFragment.
I simply in OnCreate trying to find already created fragments:
fragment = (ToDoListFragment) getFragmentManager().findFragmentByTag(LISTFRAGMENT);
frameDetailsFragment = (FrameLayout) findViewById(R.id.detailsFragment);
detailsFragment = (DetailsFragment) getFragmentManager().findFragmentByTag(DETAILS_FRAGMENT);
settingsFragment = (SettingsFragment) getFragmentManager().findFragmentByTag(SETTINGS_FRAGMENT);
Right after that I am adding my main fragment -
if (fragment == null){
fragment = new ToDoListFragment();
getFragmentManager().beginTransaction()
.add(R.id.container, fragment, LISTFRAGMENT)
.commit();
}
And clear back stask of my fragments:
if (getFragmentManager().getBackStackEntryCount() > 0 ) {
getFragmentManager().popBackStackImmediate();
}
And the MOST interesting is here -
destroyTmpFragments();
Here is the method itself:
private void destroyTmpFragments(){
if (detailsFragment != null && !detailsFragment.isVisible()) {
Log.d("ANT", "detailsFragment != null, Destroying");
getFragmentManager().beginTransaction()
.remove(detailsFragment)
.commit();
detailsFragment = null;
}
if (settingsFragment != null && !settingsFragment.isVisible()) {
Log.d("ANT", "settingsFragment != null, Destroying");
getFragmentManager().beginTransaction()
.remove(settingsFragment)
.commit();
settingsFragment = null;
}
}
As you can see, I clean manually all fragments, that FragmentManager gentfully saved for me (great thanks to him).
In log I have next lifecycle calls:
04-24 23:03:27.164 3204 3204 D ANT MainActivity onCreate()
04-24 23:03:27.184 3204 3204 I ANT DetailsFragment :: onCreateView
04-24 23:03:27.204 3204 3204 I ANT DetailsFragment :: onActivityCreated
04-24 23:03:27.204 3204 3204 I ANT DetailsFragment :: onDestroy
04-24 23:03:27.208 3204 3204 I ANT DetailsFragment :: onDetach
So in onActivityCreated make your views check for null - in case fragment is not visible. It will be deleted little bit later (It is so maybe cuz fragment manager's remove is async)
After, in activity's code we can create brand new Fragment instance, with proper layout (fargmnet's placeholder) and fragment will be able to find it's views for example, and will not produce NullPointerException
But, fragments that are in back stack are deleted pretty fast, without call to onActivityCreated (with th help of above code:
if (getFragmentManager().getBackStackEntryCount() > 0 ) {
getFragmentManager().popBackStackImmediate();
}
At last, at the end i add fragment if I am in land orientation -
if (frameDetailsFragment != null){
Log.i("ANT", "frameDetailsFragment != null");
if (EntryPool.getPool().getEntries().size() > 0) {
if (detailsFragment == null) {
detailsFragment = DetailsFragment.newInstance(EntryPool.getPool().getEntries().get(0), 0);
}
getFragmentManager().beginTransaction()
.replace(R.id.detailsFragment, detailsFragment, DETAILS_FRAGMENT)
.commit();
}
}

android listfragment created twice

Here is code where my fragment is created:
if (savedInstanceState == null) {
// During initial setup, plug in the details fragment.
FlightListFragment listFragment =
FlightListFragment.newInstance(mSearchParams);
getSupportFragmentManager().beginTransaction().add(
android.R.id.content, listFragment).commit();
}
But all fragment methods called twice! OnCreateView, OnActivityCreated, OnStart.
Why?
Thanks.
Is your fragment set to retain instance? it might be possible that you're spawning 2 instances of the fragment
Maybe try adding a call to check something like
if (getSupportFragmentManager.findFragmentByTag("myFragment") != null)
{
FlightListFragment listFragment =
FlightListFragment.newInstance(mSearchParams);
getSupportFragmentManager().beginTransaction().add(
android.R.id.content, listFragment).commit();
}

Fragment gets initialized twice when reloading activity with tabs when orientation changes

I have a problem reloading an activity with tabs and fragments when I change the orientation of my device.
Here's the situation:
I have an activity which has 3 tabs in the action bar. Each tab loads a different fragment in a FrameLayout in main view. Everything works fine if I don't change the orientation of the device. But when I do that Android tries to initialize the currently selected fragment twice which produce the following error:
E/AndroidRuntime(2022): Caused by: android.view.InflateException: Binary XML file line #39: Error inflating class fragment
Here's the sequence of steps that produce the error:
I load the activity, select tab nr 2. and change the orientation of the device.
Android destroys the activity and the instance of the fragment loaded by tab nr 2 (from now on, 'Fragment 2'). Then it proceeds to create new instances of the activity and the fragment.
Inside Activity.onCreate() I add the first tab to the action bar. When I do that, this tab gets automatically selected. It may represent a problem in the future, but I don't mind about that now. onTabSelected gets called and a new instance of the first fragment is created and loaded (see code below).
I add all the other tabs without any event being triggered, which is fine.
I call ActionBar.selectTab(myTab) to select Tab nr 2.
onTabUnselected() gets called for the first tab, and then onTabSelected() for the second tab. This sequence replaces the current fragment for an instance of Fragment 2 (see code below).
Next, Fragment.onCreateView() is called on Fragment 2 instance and the fragment layout gets inflated.
Here is the problem. Android Calls onCreate() and then onCreateView() on the fragment instance ONCE AGAIN, which produces the exception when I try to inflate (a second time) the layout.
Obviously the problem is Android is initializing the fragment twice, but I don't know why.
I tried NOT selecting the second tab when I reaload the activity but the second fragment gets initialized anyway and it is not shown (since I didn't select its tab).
I found this question: Android Fragments recreated on orientation change
The user asks basically the same I do, but I don't like the chosen answer (it's only a workaroud). There must be some way to get this working without the android:configChanges trick.
In case it's not clear, what I want to know how whether to prevent the recreation of the fragment or to avoid the double initialization of it. It would be nice to know why is this happening also. :P
Here is the relevant code:
public class MyActivity extends Activity implements ActionBar.TabListener {
private static final String TAG_FRAGMENT_1 = "frag1";
private static final String TAG_FRAGMENT_2 = "frag2";
private static final String TAG_FRAGMENT_3 = "frag3";
Fragment frag1;
Fragment frag2;
Fragment frag3;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// my_layout contains a FragmentLayout inside
setContentView(R.layout.my_layout);
// Get a reference to the fragments created automatically by Android
// when reloading the activity
FragmentManager fm = getFragmentManager();
this.frag1 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_1);
this.frag2 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_2);
this.frag3 = fm.findFragmentByTag(MyActivity.TAG_FRAGMENT_3)
ActionBar actionBar = getActionBar();
// snip...
// This triggers onTabSelected for the first tab
actionBar.addTab(actionBar.newTab()
.setText("Tab1").setTabListener(this)
.setTag(MyActivity.TAG_FRAGMENT_1));
actionBar.addTab(actionBar.newTab()
.setText("Tab2").setTabListener(this)
.setTag(MyActivity.TAG_FRAGMENT_2));
actionBar.addTab(actionBar.newTab()
.setText("Tab3").setTabListener(this)
.setTag(MyActivity.TAG_FRAGMENT_3));
Tab t = null;
// here I get a reference to the tab that must be selected
// snip...
// This triggers onTabUnselected/onTabSelected
ab.selectTab(t);
}
#Override
protected void onDestroy() {
// Not sure if this is necessary
this.frag1 = null;
this.frag2 = null;
this.frag3 = null;
super.onDestroy();
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString());
if (curFrag == null) {
curFrag = createFragmentInstanceForTag(tab.getTag().toString());
if(curFrag == null) {
// snip...
return;
}
}
ft.replace(R.id.fragment_container, curFrag, tab.getTag().toString());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft)
{
Fragment curFrag = getFragmentInstanceForTag(tab.getTag().toString());
if (curFrag == null) {
// snip...
return;
}
ft.remove(curFrag);
}
private Fragment getFragmentInstanceForTag(String tag)
{
// Returns this.frag1, this.frag2 or this.frag3
// depending on which tag was passed as parameter
}
private Fragment createFragmentInstanceForTag(String tag)
{
// Returns a new instance of the fragment requested by tag
// and assigns it to this.frag1, this.frag2 or this.frag3
}
}
The code for the Fragment is irrelevant, it just returns an inflated view on onCreateView() method override.
I got a simple answer for that:
Just add setRetainInstance(true); to the Fragment's onAttach(Activity activity) or onActivityCreated(Bundle savedInstanceState).
These two are call-backs in the Fragment Class.
So basically, what setRetainInstance(true) does is:
It maintains the state of your fragment as it is, when it goes through:
onPause();
onStop();
It maintains the instance of the Fragment no matter what the Activity goes through.
The problem with it could be, if there are too many Fragments, it may put a strain on the System.
Hope it helps.
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
setRetainInstance(true);
}
Open for Correction as always. Regards, Edward Quixote.
It seems that, when the screen is rotated and the app restarted, it is recreating each Fragment by calling the default constructor for the Fragment's class.
I have encountered the same issue and used the following workaround:
in the fragment's onCreateView begining of:
if (mView != null) {
// Log.w(TAG, "Fragment initialized again");
((ViewGroup) mView.getParent()).removeView(mView);
return mView;
}
// normal onCreateView
mView = inflater.inflate(R.layout...)
I think this is a fool proof way to avoid re-inflating of the root view of the fragment:
private WeakReference<View> mRootView;
private LayoutInflater mInflater;
/**
* inflate the fragment layout , or use a previous one if already stored <br/>
* WARNING: do not use in any function other than onCreateView
* */
private View inflateRootView() {
View rootView = mRootView == null ? null : mRootView.get();
if (rootView != null) {
final ViewParent parent = rootView.getParent();
if (parent != null && parent instanceof ViewGroup)
((ViewGroup) parent).removeView(rootView);
return rootView;
}
rootView = mFadingHelper.createView(mInflater);
mRootView = new WeakReference<View>(rootView);
return rootView;
}
#Override
public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) {
mInflater=inflater!=null?inflater:LayoutInflater.from(getActivity());
final View view = inflateRootView();
... //update your data on the views if needed
}
add
android:configChanges="orientation|screenSize"
in the manifest file
To protect activity recreate try to add configChanges in your Activity tag (in manifest), like:
android:configChanges="keyboardHidden|orientation|screenSize"
My code was a little different, but I believe our problem is the same.
In the onTabSelected I didn't use replace, I use add when is the first time creating the fragment and attach if isn't. In the onTabUnselected I use detach.
The problem is that when the view is destroyed, my Fragment was attached to the FragmentManager and never destroyed. To solve that I implemented on the onSaveInstanceBundle to detach the fragment from the FragmentManager.
The code was something like that:
FragmentTransition ft = getSupportFragmentManager().begin();
ft.detach(myFragment);
ft.commit();
In the first try I put that code in the onDestroy, but I get a exception telling me that I couldn't do it after the onSaveInstanceBundle, so I moved the code to the onSaveInstanceBundle and everything worked.
Sorry but the place where I work don't allow me to put the code here on StackOverflow. This is what I remember from the code. Feel free to edit the answer to add the code.
I think you are facing what I faced. I had a thread downloader for json which starts in onCreate() , each time I changed the orientation the thread is called and download is fired. I fixed this using onSaveInstance() and onRestoreInstance() to pass the json response in a list, in combination of checking if the list is not empty, so the extra download is not needed.
I hope this gives you a hint.
I solved this problem by using below code.
private void loadFragment(){
LogUtil.l(TAG,"loadFragment",true);
fm = getSupportFragmentManager();
Fragment hf = fm.findFragmentByTag("HOME");
Fragment sf = fm.findFragmentByTag("SETTING");
if(hf==null) {
homeFragment = getHomeFragment();// new HomeFragment();
settingsFragment = getSettingsFragment();// new Fragment();
fm.beginTransaction().add(R.id.fm_place, settingsFragment, "SETTING").hide(settingsFragment).commit();
fm.beginTransaction().add(R.id.fm_place, homeFragment, "HOME").commit();
activeFragment = homeFragment;
}else{
homeFragment = hf;
settingsFragment = sf;
activeFragment = sf;
}
}
Initiate this method in OnCreate();

Categories

Resources