I have a ViewPager of 3 fragments. All 3 fragments are of the same type, with identical layouts, but they are to hold different (text) information. I am trying to create my activity, where I create the fragments and prepare the text data that I will populate my fragments with. However, I can't seem to work with the fragments from within my activity. All the activity's lifecycle methods are executed before the fragment lifecycle methods. So if I try to update a textview in a fragment from within my activity, it won't work, because the textview is null in the fragment.
I'm going to need to make periodic updates to the fragments, so passing the data as a bundle is not an option. Plus, since I'm passing lots of text, I'm using a StringBuilder object, which is not something I can pass in a bundle (unless I make it Parcelable, which I don't want)
I think I could run a method from within my fragment class that would execute during fragment creation, but that means all 3 fragments would run this method. That's not really the level of control I'm looking for.
Is there a neat way to make this work?
Thanks
Keep references to your fragments, and let them all implement an interface with a common update-method. As an example, let's make it super clear and call the interface Updatable with one method called 'update':
public interface Updatable {
public void update(String text);
}
Now, in your Activity's onCreate, save references to your Updatables there (i.e. when you lookup or instantiate your Fragments).
It should now be trivial to update your Fragments when necessary from the Activity. Needless to say, the fragment implementation of the update code needs to do the actual update of the TextView(s).
If the update implementation is exactly the same for all your Fragments, your could save some lines of code and make a base class which implements Updatable and extends Fragment.
you might able to populating fragment fields during onActivityCreated(Bundle savedInstanceState).
Refer to this site for more information about fragments' life cycle:
http://developer.android.com/guide/components/fragments.html
You should consider using Observer pattern... there is a really great implementation which you can include as gradle dependency called EventBus:
https://github.com/greenrobot/EventBus
You can use Otto Bus to send data to your fragments from your activity.
http://square.github.io/otto/
Create a new bus in you application class
Bus bus = new Bus();
Create an event that contains your data which you'll pass to fragments.
bus.post(new MyDataEvent(data));
Register your fragment in your fragment's onResume() (Do not forget to unregister in your fragment's onPause())
bus.register(this);
And get data with subscribe in your fragment
#Subscribe
public void onDataReceived(MyDataEvent event) {
// TODO Do what ever you want
}
I hope this'll help you.
Related
I have a fragment that communicates to an activity via an interface. This is cool and all but, is it possible to have the fragment communicate with an activity that did not launch it?
The reason why is I don't want one activity to be a million lines long of code implementing all interface methods for the fragments when I could just create "helper" activities to implement all the interfaces.
Currently I am using the Google Navigation drawer template so, maybe I could create new activities and group fragments around them. Im not sure if it will break my navigation drawer if I try to launch new activites.
Because of the activity lifecycle parameters of an activity are usually separated of the activity. You can store them in the savedInstancestate and retrieve them back. This way you make sure that your activity has the right information when it is restored.
If content changes somewhere during the app lifecycle it is good to store that content somewhere permanently (SharedPreferences, Database, File).
If you want to notify several parts of your app of an event a good way to go is a Local Broadcast.
Having said that it seems strange to me that another activity than the currently running one (and the one containing your fragment) should be notified of an event. When it is resumed it would collect the necessary information and update itself.
You're breaking android development practices. Fragments are encapsulated within an Activity. And an Activity is encapsulated within itself. An Activity should not communicate with another Activity via references.
Activities are communicating via references here, so I totally would not recommend doing this. But here's how you can do what you're asking.
class HelperActivity { // implement here
public static HelperActivity context = this;
public MyFragment myFragment = new MyFragment(this); // cast to implementation
}
class NormalActivity {
void onCreate {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_attach, HelperActivity.myFragment)
.commit();
}
}
According to Android guidline, http://developer.android.com/training/basics/fragments/communicating.html
One fragment should send data to another one via the hosting Activity. I'm wondering there is a reason for that.
Since in my code, I put one variable to hold pointer to the another fragment, and assign it in onActivityCreated
//this code is in class FragmentType1, assign the pointer to the FragmentType2 for later use
#Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final FragmentManager fm = getFragmentManager();
mOtherFragment = (FragmentType2) fm
.findFragmentById(R.id.container_fragment);
}
and later if I want to set data for FragmentType2, I just call:
public setData(MyData data){
if (mOtherFragment!=null)
mOtherFragment.setData(data);
}
Everything is working fine now. What's wrong with that approach? Tks.
There's at least 2 reasons for it:
To facilitate de-coupling of fragments.
To avoid memory leaks that can happen by storing a reference to one fragment in another.
Another reason would be to maintain the state when the hosting activity is destroyed.
After recreation, you could fetch the state from the hosting activity, since the fragment itself would be unable to save it's state directly.
And last but not least, it is really hard for dynamically added fragments, especially when it comes to nested fragments, to get sure that both fragments are "alive" at the same time. Fragments life cycle is complex to manage properly and using a direct communication presumes you completely control this, which is rarely true.
The best model to my mind is to use an event bus, like Otto or EventBus or RoboGuice's bus.
I'm implementing fragments in my app. Referring to this documentation,
there is written I should use getActivity() to access activity methods but also (in the next paragraph) I should declare an interface in the fragment and let activity implement it.
Now, the second way is used for callback methods like events, but I can also use getActivity().onSomeEventHappened(), can't I?
Could someone explain me the differences? Because I cannot see differences among them.
There is no difference in the end result if you know that getActivity() will always return the type of Activity you expect.
However using interfaces is a good practice because it decouples your Fragments from a particular implementation of an Activity. So later on in the future if you decide to use your fragments with a different activity, all you have to do is have that activity implement your fragments Interface to be alerted of any fragment events.
You should always strive to have decoupled components if you want an application that is easy to extend without side effects.
You can not always simply call getActivity().onSomeEventHappened(). Just imagine this case: You have two fragments, one with ListView and other which shows image based on listItem selected. In second fragment you cannot just call getActivity().onListItemClicked(), because your activity has no such method, but if activity implements interface and catches those event from the first fragment, then you are able to pass info about event to the second fragment and how the right image.
I am building a tab interface using Action bar and fragment. I would need assistance in sending data from container activity to the fragment.
To elaborate, I have job object in container activty. And I have created few tabs based on the information in job object (like company details, experience details etc). I need to pass the job object to these fragments so that it can display respective information.
I have created container activity and tab fragments. I would need an example on how to pass the object across them. I cannot use intent.putExtra. Can I access parent container's object from fragment?
Any help shall be appreciated.
Thanks.
Make the method in your activity, e.g getJob that will return the Job object and its information
MyActivity extends Activity{
Job mJob;
public Job getJob(){
return this.mJob;
}
}
then in your Fragment you do this:
MyFragment extends Fragment{
#Override
public void onActivityCreated(){
super.onActivityCreated();
((MyActivity)this.getActivity()).getJob();
}
}
use getActivity and the method getJob(); to get the object
There are multiple ways of achieving this.
Make a static variable to hold your data and access that data from inside the fragments - this is the most fast but it creates bad design patterns if used improperly.
A way of Fragment-to-Fragment communication possible through the parent Activity is posted here: http://developer.android.com/training/basics/fragments/communicating.html You can use the sample code to just do a Activity - Fragment data send.
The top voted answer here: Accessing instance of the parent activity? mentions a way to avoid using static data (1.) and contains source code examples using ActivityGroup
"If you need access to some values in your First activity without
making a static reference to it, you could consider putting your
activities in an ActivityGroup."
What you choose is your preference, these are just a few options!
Edit: I'm not sure if number 3 will work with fragments since I haven't tested a method similar to it, the example is Activity - Activity communication.
I'd like my activity to send the same event to multiple fragment. Instead of making my activity calling each individual fragment method : FragmentA.DoTask(), FragmentB.DoTask(), FragmentC.DoTask(), etc... I'd like rather make my activity send only one event and then make the fragment listening to this event.
On the developpers docs they make the activity "listen" to the fragment but then the activity calls the fragments' methods. Is it possible the other way around : to make the fragments "listen" to the activity.
Thanks
You could Use EventBus system.
Description on RRiBbit (though this library not related to Android) define it precisely:
An Eventbus is a mechanism that allows different components to
communicate with each other without knowing about each other. A
component can send an Event to the Eventbus without knowing who will
pick it up or how many others will pick it up. Components can also
listen to Events on an Eventbus, without knowing who sent the Events.
That way, components can communicate without depending on each other.
Also, it is very easy to substitute a component. As long as the new
component understands the Events that are being sent and received, the
other components will never know.
So what exactly is a component here? Well, a component could be
anything. In most Eventbuses, they are Java Objects. They send Events
and they also listen to Events.
In Android you can use EventBus.
I don't think there is a way around it. The word "listen" in the docs is more metaphorical. You have to call all fragments explicitly.
The closet thing you can get, is to have a list of Fragment maintained in your Activity class. e.g.: create a customized Fragment class:
public class MyFragment extends Fragment{
public abstract void doTask();
}
Have all your Fragments inherited from this class.
public class FragmentA extends MyFragment{
#Override
public void doTask(){
//exec code here
}
}
In your Activity class, each time you create a Fragment, add it to a List too. When the event occurs, call all Fragments.
public class MyActivity extends Activity{
List<myFragment> mFragmentList = new ArrayList<MyFragment>();
public void addFragment(MyFragment fragment){
mFragmentList.add(fragment);
}
public void onEvent(){
for(MyFragment fragment:mFragmentList){
fragment.doTask();
}
}
}
Note: If you don't have that many Fragments, this solution can be a overkill.