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.
Related
I wonder the difference between two ways of transfering data from activity to fragment.
One is using getArgument() and setArgument(). I can transfer data using these methods at Fragment's contruction time.
Another is using getActivity() method. Like this way
((HostActivity)getActivity()).getXXX()
After declaring getter method of data Fragment may use, call this method in fragment through getActivity() and Type casting.
I think second one is easier and convenient. Because get/setArgument() can be called only Fragment's contruction time.
So, How to apply these 2 way to sending and getting data between Activity and Fragment?
A Fragment represents a behavior or a portion of user interface in an
Activity. You can combine multiple fragments in a single activity to
build a multi-pane UI and reuse a fragment in multiple activities.
Because fragment can reuse in multiple activity, if you use getActivity() with type casting, you must check instanceOf activity before call method. And each of activity use that fragment, you must implement method getXXX().
Use newInstance method in fragment, you only pass require parameter for it.
If you create fragment for individual activity, you can apply 2 ways transfer data.
The fragment has an independent lifecycle from activity with specific threads, functions and handlers. So you can use getters/setters Activity variables like a global variables and bundle data (arguments) to independent fragment variables.
I have a fragment that is always visible. I don't understand why I should use bundles to pass data to it from activity.
Most of the questions here recommend this method of passing data:
Bundle bundle=new Bundle();
bundle.putString("name", "From Activity");
Fragmentclass fragobj=new Fragmentclass();
fragobj.setArguments(bundle);
I prefer creating Fragment object in OnCreate function of activity and then use this object to display fragment(FragmentTransaction.add). As I have refence to this fragment I can create create function showName() in it and call it from activity like that:
myFragment.showName("name");
Is there anything wrong with this approach?
The Android documentation states:
Every fragment must have an empty constructor, so it can be instantiated when restoring its activity's state. It is strongly recommended that subclasses do not have other constructors with parameters, since these constructors will not be called when the fragment is re-instantiated; instead, arguments can be supplied by the caller with setArguments(Bundle) and later retrieved by the Fragment with getArguments().
That's why it's better to use a bundle and set the parameters of the Fragment this way, it's easier for the system to restore its values when the fragment is re-instantiated.
Now, I wouldn't use myFragment.showName("name"); because you don't know if the lifecycle of the fragment has already finished (attached to the activity and inflated the views), so instead, I would call the showName("name") in the onActivityCreated or onViewCreated callbacks.
Applications should generally not implement a constructor. The first place application code can run where the fragment is ready to be used is in onAttach(Activity), the point where the fragment is actually associated with its activity. Some applications may also want to implement onInflate(Activity, AttributeSet, Bundle) to retrieve attributes from a layout resource, though should take care here because this happens for the fragment is attached to its activity.
There's nothing wrong with this approach for setting one off data values, you just need to be careful to make sure that the view that you want to set your name on actually exists at the point that the showName method is called.
Part of the reason that using Bundles to pass information is popular is that they can hold all types of data using keys and also they can easily be used to pass view states around during device rotation. Ultimately it's a matter of preference and exactly what your use case is.
When app is in background, the Fragment can recreated (eg: by change the theme (light/dark), language, ...).
So if you dont pass data use Bundle to Fragment, your Fragment will not have this data when it recreated
For some reason my understanding was that a headless Fragment lives for the duration of your application. With this understanding, in my attempt to persist an object between startActivityForResult() I put the object in a Headless Fragment like this
private HeadlessFragment modelFragment;
modelFragment = (HeadlessFragment)
getSupportFragmentManager().findFragmentByTag(Constants.HEADLESS_FRAGMENT_TAG);
if (modelFragment == null){
modelFragment = new HeadlessFragment();
}
modelFragment.setInvoice(invoice);
I can confirm that the custom object was set, however when I go to the next activity and try to get the same object by calling findFragmentByTag with same tag the object is null.
Does a Headless Fragment survive between two Activities life cycle? I did set setRetainInstance(true) on that Headless Fragment. I was hoping that I will not have to implement Parceable on my custom object.
For some reason my understanding was that a headless Fragment lives for the duration of your application.
No. Fragments are owned by activities and are not application-wide constructs.
I can confirm that the custom object was set, however when I go to the next activity and try to get the same object by calling findFragmentByTag with same tag the object is null.
There are at least two reasons for this:
First, at least in the code that you are showing, you never add the fragment to the FragmentManager via a FragmentTransaction. As such, the activity that created the fragment will not be able to find the fragment via findFragmentByTag(), because the FragmentManager does not know about it.
Second, each activity has its own FragmentManager, and fragments from one activity are not accessible in another activity.
I was hoping that I will not have to implement Parceable on my custom object.
Then don't pass the object. Pass the information (e.g., a key or ID) by which the other activity can retrieve the object (from a singleton POJO cache, by querying the database, etc.).
Or, do not make them separate activities, but have them as separate (regular) fragments in one activity.
Or, implement Serializable, though Parcelable executes more quickly.
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 very new to Android programming (and Java for that matter) coming from an iOS background.
What I am trying to do, is pass a pointer to a Fragment from one Activity to another.
Basically, I have a starting activity called BeginActivity that handles a couple of Fragments for login and register screens. Once logged in, I load up the main activity of the app called TabsFragmentActivity using this code:
public void loggedIn() {
Intent intent = new Intent(this, TabsFragmentActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
this.startActivity(intent);
finish();
}
I'm using FLAG_ACTIVITY_CLEAR_TOP as I dont want the user to go back without actually logging out first.
Now the problem:
In BeginActivity I have a pointer to a fragment that holds the users data. I am using it like a singleton, that the first few view fragments can access from BeginActivity.
I need to pass this same object to the new TabsFragmentActivity before I call finish() on it.
How do I do this?
I know I can use putExtra() but I believe that is just for strings etc.. and not other Fragments.
Is there a way in the newly created TabsFragmentActivity that I can reference the BeginActivity to 'grab' the pointer?
Thanks
First of all, you should be sure about Fragments and Activity life cycle.
Fragments are designed to be reusable UI complex components. They look like activity, but you can reuse. So,you can have as many activities you need containing the same fragments, but not the same instances of these fragments.
If you just want to pass you user data for another activity you must use Bundle and putExtra(). Depending of the user data type can be necessary implements Serializable or Parcelable Interfaces, as #gheese said.
If you want to use the same UI appearence of your fragment on two or more activities, besides use Bundle and putExtra. Each activity that you want this behavior must contains a field whose is a Fragment and in the moment of starting this fragment you can use getActivity().getIntent().getExtra to get the user information and populate your fragment.
Basically you need to be able to pass your class via the intent, look at Serializable / Parcelable interfaces
This question has the answer you require
How to pass an object from one activity to another on Android