I am extremely new to Android development. I basically have an activity with some buttons, for example "seeTreePicture" and "seeSeaPicture". When I press a button, I want to use a fragment I called "ContentViewer" to display a random tree/sea picture, and also have buttons under the picture to destroy the ContentViewer fragment instance and go back to the menu. The issue is, if I try to use a Fragment Transaction anywhere other than onCreate() of the activity, I get a null pointer exception when I try to access the view in the fragment.
My activity and things related to the fragment:
public class SeeActivity extends AppCompatActivity {
DisplayFragment displayFragment;
Button seeTreeButton;
Button seeSeaButton;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_see);
seeTreeButton = findViewById(R.id.seeTreeButton);
seeSeaButton = findViewById(R.id.seeSeaButton);
displayFragment = new DisplayFragment();
seeTreeButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.fragmentContainer, displayFragment);
fragmentTransaction.commit();
fragmentTransaction.addToBackStack(null);
displayFragment.changeImage(randomTree);
}
});
}
}
In my fragment, change image simply changes the image source of the ImageView:
public void changeImage(int treeResource)
{
img = getView().findViewById(R.id.imageView);
img.setImageResource(treeResource);
}
I get a null pointer exception for trying to access the view from getView() in the fragment, meaning that onCreateView wasn't invoked. Yet if I put the same transaction in the onCreate() method of the activity, it works. What am I doing wrong?
The issue with your code is that you are accessing getView() of your fragment before the fragment has gone through the initialization of the view. The reason why you don't have the crash when you execute the transaction in your activity's onCreate() method is that by the time you click on a button your fragment has already gone through onCreateView() and initialized its view. Check out fragment lifecycle guide and bear in mind that you should not access your fragment view before it was created or after it was destroyed. For more information about why your fragment view is not initialized instantly check out this guide.
As for the solution, consider setting arguments for your fragment before adding it to your transaction like here:
Bundle args = new Bundle();
args.putInt(DisplayFragment.IMG_RESOURCE_ARG, randomTree);
displayFragment.setArguments(args);
Then in your onCreateView() or onViewCreated() methods of your fragment restore the arguments like here and set the image resource:
#Override
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
int resourceId = requireArguments().getInt(IMG_RESOURCE_ARG);
img = view.findViewById(R.id.imageView);
img.setImageResource(resourceId);
}
Because the fragment transaction doesn't occur instantly. It occurs async. So the actual work of creating views hasn't occurred yet. Your options are to either use commitNow() instead of commit (which will do it synchronously, but require much more time and possibly cause your app to visibly pause) or to wait for the fragment transaction to actually complete. That can easily be done by putting it in a Runnable and passing that runnable to runOnCommit
Related
I am working on an application and there is one specific thing that is bothering me. Let's just say I have one activity and 2 fragments.FragmentA and FragmentB and FragmentA gets attached when activity starts.
I want to save the fragment data and fragment state when orientation changes occur.I have successfully saved fragment data using OnSavedInstanceState method. Now I want to save fragment state in the activity so that if orientation change occurs I want to be on the fragment I was (in my case either FragmentA or FragmentB depends on which was showing before config changes occur).
This is how I am saving the fragment state in the Activity:
#Override
protected void onSaveInstanceState(Bundle outState) {
// Save the values you need into "outState"
super.onSaveInstanceState(outState);
outState.putLong(SS_DATE, userDate.getTime());
android.support.v4.app.FragmentManager manager = getSupportFragmentManager();
Fragment currentFragment = this.getSupportFragmentManager().findFragmentById(R.id.content_container);
manager.putFragment(outState, "currentFragment", currentFragment);
}
And this is how I am retrieving on which fragment I was when the orientation change occurred:
#Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
FragmentManager manager = getSupportFragmentManager();
#SuppressLint("CommitTransaction")
FragmentTransaction transaction = manager.beginTransaction();
if (savedInstanceState != null) {
Fragment MyFragment = (Fragment) manager.getFragment(savedInstanceState, "currentFragment");
if (MyFragment instanceof FragListStudentsAttendance) {
Log.v("onRestore", FragListStudentsAttendance.TAG);
}else if (MyFragment instanceof FragGetClassesForAttendance){
Log.v("onRestore", FragGetClassesForAttendance.TAG);
if(MyFragment!=null) {
mFragGetClassesForAttendance = (FragGetClassesForAttendance) MyFragment;
}else{
mFragGetClassesForAttendance = new FragGetClassesForAttendance();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// mFragGetClassesForAttendanceNew.setRetainInstance(true);
// transaction.replace(R.id.content_ssadmin_container, mFragGetClassesForAttendanceNew, "FragGetClassesForAttendance").addToBackStack(null);
transaction.add(R.id.content_ssadmin_container, mFragGetClassesForAttendance, FragGetClassesForAttendance.TAG);
//transaction.replace(R.id.newEnrollmentMainContainer, mFragNewEnrollmentResults).addToBackStack("FragNewEnrollments");
transaction.commit();
mFragGetClassesForAttendance.setDate(userDate);
}
}
}
}
Now
Scenario 1:
If I am on fragment A and I rotate the device every thing works fine as it should. Like fragment have web services which loads the data into listview so I check if data exist then there is no need to run the web service and that working for now
Scenario 2:
If I am on fragment B and orientation change occurs everything works fine as it is supposed to be on fragment B. Now When I press back button Fragment A gets called again and all the data also comes from service. I think this shouldn't happen because it was supposed to be in BackStack and it's data was saved. So what Should I do now here?
Scenario 3: On FragmentB I have noticed that when I rotates the device the saveInstanceState function of FragmentA also gets called. Why it is so? where as I was replacing the FragmentB with FragmentA ?
Some Confusions:
Let me talk about some of the confusions also , maybe someone clear it to me although I have searched and read a lot about fragment and activity life cycle,
Actually I want to save the data per activity and fragment on device rotation. I know how to do it with activity(how to save states) so I also know how to do it in the fragment (save state of fragment views) now I am confused how to tell activity which fragment was showing and which to go after config changes(rotation) ? also what happens to FragmentA if I am on FragmentB Does its get attach and detach again and again in background?
I got your problems and confusions. I think the life cycle of fragment is confusing you. and indeed it will confuse you.
You need to learn different situations.
1. Fragment Life cycle when it is in foreground (attaching and detaching with activity) . Please keenly observe all the methods that will call i.e OnSaveInstance,onCreateView,OnDestroyView,onDestroy
2. Fragment life cycle when it is in background (observe the methods stated above)
3. Fragment life cycle when it is added to backstack (and not in foreground)
I am quite sure you are confused with the point number 3. As when the fragment is added to backstack it never gets destroy. So rotating device twice will set the ffragment data to null. I think you are restoring data on ActivityCreated or on onViewCreated ,
Ill suggest you to restore the fragment data in the oncreate. this will work for you when your fragment is coming back to foreground from the backstack .
Example
private List<String> mCountries;</pre>
#Override
public void onCreate(Bundle savedInstanceState)
{
if (savedInstanceState != null)
{
// Populate countries from bundle
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_countries, container, false);
if (mCountries == null)
{
// Populate countries by calling AsyncTask
}
return view;
}
public void onSaveInstanceState(Bundle outState)
{
// Save countries into bundle
}
Hope this will clear your confusions.
I have an Android application with a navigation drawer. My problem is that some fragment takes few second to load (parser, Map API). I would like to load all my fragment when the app starts.
I'm not sure if it is possible or a good way to do it, but I was thinking of create an instance of each of my fragments in the onCreate method of the main activity. Then, when the user select a fragment in the navigation drawer, I use the existing instance instead of creating a new one.
The problem is that it does not prevent lag the first time I show a specific fragment. In my opinion, the reason is that the fragment constructor does not do a lot of operation.
After searching the web, I can't find an elegant way to "preload" fragment when the application starts (and not when the user select an item in the drawer).
Some post talks about AsyncTask, but it looks like MapFragment operation can't be executed except in the main thread (I got an exception when I try: java.lang.IllegalStateException: Not on the main thread).
here is what I've tried so far:
mFragments = new Fragment[BasicFragment.FRAGMENT_NUMBER];
mFragments[BasicFragment.HOMEFRAGMENT_ID] = new HomeFragment();
mFragments[BasicFragment.CAFEFRAGMENT_ID] = new CafeFragment();
mFragments[BasicFragment.SERVICEFRAGMENT_ID] = new ServiceFragment();
mFragments[BasicFragment.GOOGLEMAPFRAGMENT_ID] = new GoogleMapFragment();
When an item is selected in the nav drawer:
private void selectItem(int position) {
Fragment fragment = mFragments[position];
// here, I check if the fragment is null and instanciate it if needed
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
mDrawerList.setItemChecked(position,true);
mDrawerLayout.closeDrawer(mDrawerList);
}
I also tried this solution; it allows to prevent a fragment from being loaded twice (or more), but it does not prevent my app from lag the first time I show it. That's why I try to load all fragments when the application starts (using a splash-screen or something) in order to prevent further lags.
Thanks for your help / suggestion.
You can put your fragments in ViewPager. It preloads 2 pages(fragments) by default. Also you can increase the number of preloaded pages(fragments)
mViewPager.setOffscreenPageLimit(int numberOfPreloadedPages);
However, you will need to rewrite your showFragment method and rewrite back stack logic.
One thing you can do is load the resources in a UI-less fragment by returning null in in Fragment#onCreateView(). You can also call Fragment#setRetainInstance(true) in order to prevent the fragment from being destroyed.
This can be added to the FragmentManager in Activity#onCreate(). From there, Fragments that you add can hook in to this resource fragment to get the resources they need.
So something like this:
public class ResourceFragment extends Fragment {
public static final String TAG = "resourceFragment";
private Bitmap mExtremelyLargeBitmap = null;
#Override
public View onCreateView(ViewInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return null;
}
#Override
public void onStart() {
super.onStart();
new BitmapLoader().execute();
}
public Bitmap getExtremelyLargeBitmap() {
return mExtremelyLargeBitmap;
}
private class BitmapLoader extends AsyncTask<Void, Void, Bitmap> {
#Override
protected Bitmap doInBackground(Void... params) {
return decodeBitmapMethod();
}
#Override
protected void onPostExecute(Bitmap result) {
mExtremelyLargeBitmap = result;
}
}
}
Add it to the fragment manager in the Activity first thing. Then, whenever you load your other Fragments, they merely have to get the resource fragment from the fragment manager like so:
public class FakeFragment extends Fragment {
#Override
public View onCreateView(ViewInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final ResourceFragment resFragment = getFragmentManager().findFragmentByTag(ResourceFragment.TAG);
Bitmap largeBitmap = resFragment.getBitmap();
if (largeBitmap != null) {
// Do something with it.
}
}
}
You will probably have to make a "register/unregister" listener set up because you will still need to wait until the resources are loaded, but you can start loading resources as soon as possible without creating a bunch of fragments at first.
To preload fragments, attach() can be used. So in OP's case it will be:
ft.attach(fragment).commit();
Make sure to store the fragment somewhere and use that one the next time ft.replace() is called.
I have an Activity with a FrameLayout and need to show different fragments based on user input.
The code I use for showing a fragment is this:
private void showFragment(Fragment fragment, Bundle args, boolean addToBackStack) {
if (args != null) {
fragment.setArguments(args);
}
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.activity_open_translate, R.anim.activity_close_scale);
fragmentTransaction.replace(R.id.main_frame, fragment);
if (addToBackStack) {
fragmentTransaction.addToBackStack(fragment.getClass().getName());
}
fragmentTransaction.commit();
}
This is called as :
if (contactPickFragment == null) {
contactPickFragment = new ContactPickFragment();
}
showFragment(contactPickFragment, args, true);
All this works fine. Now if the user goes into one fragment presses back and returns back to the same fragment, all my views inside stay the same. For example, I have an EditText inside the fragment and the user edits something inside. If the user comes back to the fragment, the same text persists. I do not want this to happen. How do I reset everything in the view?
I have added code within the Fragment's onCreateView() to clear the text, and from debugging I see that this is being called, but the text never gets cleared. What am I doing wrong here?
If you don't want the data from the previous instance to appear, simply create a new instance of ContactPickFragment each time you show it.
Clearing data in onCreateView() has no effect because view state is restored AFTER onCreateView(). Your Fragment has no view before onCreateView() and so Android cannot possibly apply the previous state any earlier. Values set on the views during onCreateView() will be overwritten by their previous values.
As a general answer, there is no way to "refresh" the view of a Fragment, other than replacing the fragment with another instance of itself (possibly initialized with the parameters that you want to refresh/update).
You can reuse your fragments and refresh the state of your views. You just can't do it from onCreateView as #antonyt correctly points out.
Instead, override onViewStateRestored and set up the state of your views the way you'd like from there.
Something like:
#Override
public void onViewStateRestored(Bundle savedInstanceState) {
super.onViewStateRestored(savedInstanceState);
View view = getView();
// Code to call view.findViewById to grab the views you want
// and set them to a specific state goes here
}
There are advantages to reusing fragments. Not the least of which is that if you have a memory leak with your fragment (which is easier than you may think to accomplish,) you will exacerbate the problem by creating myriads of them.
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();
EDIT
So it seems that my Fragment is retained in the FragmentManager which tries to reinitialize it. Still not sure why it isn't destroyed with the Activity. As for the loading message, this is displayed when the ListView does not have an adapter set. I however, set the adapter items in onCreate and onResume so I'm not sure why this loading screen is showing. Still open to any explanations of this behavior
Original
I'm playing around with fragments and noticed a weird error that is popping up when I change the orientation of the screen. This error should not be happening though because all the data is recreated in the onCreate when the screen orientation is changed. Also, the fragment onResume() is called twice after the rotation. Here's my steps for creating the error and how the debugger is hitting the functions.
Activity: onCreate()
Activity: onResume()
Fragment: onResume()
Rotate Screen
Activity: onCreate()
Activity: onResume()
Fragment: onResume() (items are null even though Activity.onResume() set them)
Fragment: onResume() (items are not null, why is this being called twice?)
After this last fragment onResume is hit, the tablet displays a "Loading..." message and icon. Why is the data not displayed in the list any more? My suspicions are that the onCreate is creating a second fragment. The first fragment looses its data because of the orientation destroying the views, the second fragment gets the data and the loading screen is the first fragment with no data items and the second fragment is hidden. I may be wrong. Why aren't the fragments all destroyed when the screen is rotated like the Activity? Please do not critique the code unless its to solve this specific issue. I'm not actually making an app, I'm experimenting with fragment functionality. Thanks!
Main Activity
private ArrayList<Object> items = new ArrayList<Object>();
private MyListFragment mylistFragment;
public MainActivity() {
items.add("Hello");
items.add("World");
items.add("Goodbye");
}
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
mylistFragment = new MyListFragment();
mylistFragment.setItems(items);
ft.add(R.id.container, mylistFragment);
ft.commit();
}
#Override
public void onResume() {
super.onResume();
mylistFragment.setItems(items);
mylistFragment.getListView().setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(MainActivity.this, ((TextView)view).getText(), Toast.LENGTH_LONG).show();
}
});
}
List Fragment
private List<Object> items = null;
private Boolean isSet = false;
#Override
public void onResume() {
super.onResume();
if( !isSet && items != null) {
setListAdapter(new ArrayAdapter<Object>(getActivity(), R.layout.item, items));
isSet = true;
}
}
public void setItems(List<Object> items) {
this.items = items;
if( this.isResumed() ) {
setListAdapter(new ArrayAdapter<Object>(getActivity(), R.layout.item, items));
isSet = true;
} else {
isSet = false;
}
}
The FragmentManager will automatically recreate the Fragment for me. However, the data inside them needs to be reinitialized. Therefore my code was creating a second fragment. The loading screen was because the first fragment no longer had its adapter of items and the second fragment was hidden. What a headache, I almost think the FragmentManager should destroy the Fragments so you can recreate them with the data they hold.
I believe this is why in the various demos via Diane and other google examples... they typically have some way of helping hte initialization via a static method... and setArguments.
IIRC, the fragment creation stuff will call the default constructor to create, but only knows about populating with appropriate data via setArguments. In the example on the android development blog... the id was being passed from the listactivity, to the details fragment. After setParams is called, the fragmentManager should be able to reanimate it.
Documentation seems to be sparse on the subject and I'd like to be doing things right, so if you guys have feedback, I'd appreciate it.
It seems that using FragmentActivity from the support library saves and restores instance automatically. Therefore, only do your fragment transactions if savedInstanceState is null.
For example, in your FragmentActivity's onCreate(), do the following:
if(savedInstanceState == null){
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, mFragment).commit(); //mFragment is your own defined fragment
}