I have an object in my main activity that stores a bunch of data from an XML document and I want to be able to access that information on several different fragments to display the information. How can I go about doing that
Your object can implements java.ioSerializable . Than you are allowed to pit an instance of this object into the android bundle with putSerializable. Finally you can use the setArguments method to pass the bundle instance through your fragment
Related
I have an object that implements Parcelable interface.
Case 1:
I have an Activity A and B. I would like to pass object from A to B using Intent.putExtra(String name, Parcelable value) so in Activity B when I get object and change some fields and get back to A the field of object remain the same. That mean after pass object via Intent.putExtra(String name, Parcelable value) a new instance of object will be created.
Case 2:
I have an Activity that contains a list of objects, also that activity has ViewPager with fragments. I create fragments and pass to it object Bundle.putParcelable(String key, Parcelable value) so when I get object and change some fields (int Fragment) the object that place in a list(in Activity) also changed. So that mean object hasn't been created just reference has been passed to fragment.
Can someone explain that strange behavior about Bundle.putParcelable(String key, Parcelable value) and Intent.putExtra(String name, Parcelable value)?
That's because Intent mechanism and the set/getArguments() of Fragment works differently.
When Activity contains Fragment it has direct reference to the Fragment, and setting a bundle on Fragment is simple set on the Fragment object, that's means that the Bundle object reference but the Activity is the same as the one in the Fragment. Actually you don't necessarily need this mechanism, as you're Activity probably controls the Fragments and knows their type, so you can just pass arguments by exposing a method on your Fragment. the benefit and the similarity to intent is that using provided set/getArguments() is retaining those arguments across fragment destroy and creation, those it uses Bundle and Parcelable, which are data that the system can stored outside your control.
But, Intent and Activity are different, Activity have no object reference to other Activities, and communicating must go through the system, and thus - unlike Activity/Fragment must be done with Parcelable/Bundle data that the system can serailze/deserialze.
When you sent an Intent to a different Activity your Parcelable goes through the system and get serailze/deserialze thus - you have a different Object reference in 2 differnt Activities. this is not happening when you setArgument on Fragment as Activity/Fragment has no boundaries like different Activities.
This difference is not about putParcelable vs. putExtra.
An Activity which accepts an Intent or a Fragment which accepts arguments should not mutate incoming data because it may be passed by reference (as an optimisation), or may be recreated from Parcel (when it's necessary).
If you want to return some data from Activity, use startActivityForResult() and setResult(). If you want to return some data from Fragment, use getActivity() to get a host Activity from a Fragment and communicate with it.
What is the best way to access data members of an activity from its fragment
Some ways which I know include -
Create an interface in the Fragment which the Activity will implement. The interface will have the methods to access data members of the Activity.
Directly access using ((Activity)getActivity).getXXX() from the fragment.
Pass the data members or custom parcelable class to newInstance method of the fragment and set the fragment arguments as the class for e.g. -
Bundle args = new Bundle();
args.putInt("num", num);
f.setArguments(args);
and later we can get the arguments using getArguments()
Which method is the best and what are the drawbacks of each?
Actually the combination of the first and third method is the best. The second one should be avoided at all cost since this strongly couples the Fragment to a specific Activity. This will defeat one of the main advantages of Fragments namely being able to use it in different Activities (plug and play).
As for the first and third method.
- The first one is how you will usually communicate from the Fragment to your Activity.
- The third one is how you'll usually instantiate your Fragment while passing data to it from your Activity. When you already have an instance of your Fragment running you'll have to fall back on your first method.
I have specific fragments which will perform actions in a background thread (or will start activities for result) and I would like to set a callback (from the hosting activity), to get some data from the fragment after such actions, for instance:
FragmentManager fm = getFragmentManager();
TakePhotoFragment takePhotoFragment = (TakePhotoFragment) fm.findFragmentById(R.id.frTakePhoto);
takePhotoFragment.setListener(new TakePhotoFragment.OnNewBitmapListener() {
#Override
public void onNewBitmap(Bitmap bitmap) {
// do things in the activity
}
});
I have read that it is safer to feed arguments into the fragment using Bundles, but there are several cases which I would like to pass object references rather than simple String data using Bundles. Is there any problem in getting a reference using a FragmentManager and calling methods on it? If yes, which are the workarounds?
If your fragment is not yet created use bundle, otherwise use instance one.
It is safer to pass bundle becoz when android recreate your fragment your pass data in bundle is not lost.
If you use the instance one to pass data you have to save and restore that data yourself
Note: you can pass complex object through bundle by making them parcelable or serializable.
Also passing big data in bundle is not recommended
If your fragment is defined in xml you can get the instance by findFragmentById() of fragmentManager class
I have a custom class called Data which contains all the data. The main activity creates two fragments. I have a field in the main activity like this:
private Data data = new Data();
The fragments are created with this method:
private ArrayList<Fragment> getFragments() {
ArrayList<Fragment> fragments = new ArrayList<Fragment>();
fragments.add(new fragment_one());
fragments.add(new fragment_two());
return fragments;
}
I need to pass the data field to the fragments, so the fragments can acces the methods of Data.
I tried by creating a bundle, but I can't pass a custom class. What can I do?
Bundles can accept custom classes, if they implement either Parcelable or Serializable, Parcelable is faster but more work to implement and Serializable is easier to implement, but slower.
Then you can do this:
Bundle bundle = new Bundle();
bundle.putSerializable("MyData", data);
fragment_one.setArguments(bundle);
Now fragment_one will have access to data in it's onCreate(Bundle bundleHoldingData) method.
Another option is to have a public setter in your fragment that takes in data. Benefit of this is you don't have to wait till data is ready to add the fragment.
Data needs to either implement Parcelable or Serializable.
You can then either use bundle.putParcelable() or bundle.putSerializable() to pass the data to both fragments via the setArguments() method.
You should not pass references to fragments, all your data should be passed using setArguments (unless your fragment is retained). The reason is that android might destroy your fragment during configuration change, and recreate it during activity creation.
So you should either pass your data inside setArguments, or give access to it using singleton class, ie. application class.
[edit] - havent tried this myself but you can find online tools to make your data class parcelable, here is one: http://devk.it/proj/parcelabler/
You can set a static object depending how much data you keep and careful with the memory leaks. That way you can reach it within the fragments. But making the data parcelable and pass it with the bundle is always a better choice.
One option is to provide an accessor for the data on the Activity class. Then, in your fragment, you call getActivity(), cast it to the derived type, and get the data, as needed.
This of course creates a dependency from your fragment to the activity, but if it's not meant to be a generic, re-usable fragment, it would be very simple and straightforward to implement and means you can get a reference to the current Activity data and not a copy like the Bundle / Parcelable / Serializable strategy would.
I'm trying to do this without using a bundle. The reason I am trying to avoid that is because the data is pulled from a database online and I don't want the fragments to not load while it's waiting to get the data. From what I can tell I can only do the bundle arguments if the data is ready to send before any of the fragments are created.
This would cause a problem because the data isn't pulled from the database until the ViewPager is created. I can successfully pass the variables, but like I said the data isn't there instantaneously. This ends up sending a zero value through the bundle variable.
Am I incorrect in assuming you can't still pass bundles after the fragment creation? Aside from Bundles, what would be the best way to send a variable from the ViewPager to the Fragment? Would storing the data in the cache be a good option?
You can setup a method within your fragment to be called from your activity once you have retrieved said data from database. Then within your fragment method you can load it to where ever you need and however you need.
Only thing you have to be concerned about is the static context of the method.
You could cast your Activity to Fragment.getActivity then invoke a getter for your data.
Alternatively, you could use a Loader and implement LoaderManager.LoaderCallbacks in your Fragment to return the data from your database, but only initialize it once the data has been set.
I suppose you could also show some sort of loading Fragment, then replace it with the other after the data is set. You could use a Bundle in that case.
There are lots of ways to solve this. And yeah, the Fragments arguments can't be passed after creation, unless you call Fragment.setArguments before it's attached to the Activity.
Source
Fragment.instantiate
Fragment.setArguments