Fragment And Activity Working Together
Written by Mike James   
Thursday, 27 March 2014
Article Index
Fragment And Activity Working Together
Using Bundles
Persisting State
An Interface Callback
Persisting Fragments

 

The Fragment template creates a static factory method for your Fragment unless you untick the option Include fragment factory methods.

The factory methods starts off by creating and instance of your Fragment class using the default parameterless constructor:

public static BlankFragment newInstance(
                   String param1, String param2) {
 BlankFragment fragment = new BlankFragment();

The factory method is static and hence it has to be called from the class rather than any instance of the class. That is you call it using:

BlankFragment myFrag=
      BlankFragment.newInstance("param1","param2")

The parameters param1 and param2 are stand-ins for you to change to the number and type of parameters you actually want to use. The parameters are the arguments you are going to use to customise your Fragment's View hierarchy. 

The template also automatically adds two private fields for you to to store the parameter values in if you need to: 

private String mParam1;
private String mParam2;

Again you have to change the type, name and number of these variables to fit in with whatever you are passing as parameters. 

Now we come to the tricky part. 

You don't want to make use of the parameters passed to the factory method when the Fragment is constructed because it can be destroyed and recreated by the system via the parameterless constructor.

Just as in the case of an Activity you have to be prepared to re-set the state of a Fragment when the system restores it and to do this we have to make use of a Bundle. This is exactly what we have to do with the Activity's state and the way a Bundle works was described in Chapter 4.

The basic idea is that a Bundle is just an object that you can use to store simple values as name, value pairs. 

To make this slightly easier, the template creates some default names for each for the parameters it has generated:

private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";

Again you need to change these names to suit your actual parameters. For example if the first parameter was a count of something you might edit the first line to read:

private static final String ARG_COUNT = "count";

The factory method now creates a Bundle object and stores the parameters in it:

Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);

and again you need to edit these lines to use appropriate names for the parameters. You also need to change putString to what ever data type you are using of each parameter. Notice that you can only store a limited range of data types in a Bundle - you can't use it to pass an object say (unless the object can be serialized).

Finally you make use of the Fragment's setArguments method to store the Bundle:

 fragment.setArguments(args);
 return fragment;
}

Ok so now you have stored the Fragment's arguments safely in a Bundle and you can rely on the system to take care of them even if the Fragment is removed. We have yet to see how to make use of the Bundle but this is fairly easy. 

The complete factory method is:

public static BlankFragment newInstance(
                 String param1, String param2) {
 BlankFragment fragment = new BlankFragment();
 Bundle args = new Bundle();
 args.putString(ARG_PARAM1, param1);
 args.putString(ARG_PARAM2, param2);
 fragment.setArguments(args);
 return fragment;
}

For the sake of completeness let's see how the same job could have been done using a parametrized constructor instead of a factory method:

public BlankFragment(String param1, String param2){
 Bundle args = new Bundle();
 args.putString(ARG_PARAM1, param1);
 args.putString(ARG_PARAM2, param2);
 this.setArguments(args);
}

To sum up:

if the system needs to destroy the Fragment it will preserve its Arguments bundle and make it available again when it restores the Fragment. 

Using The Bundle

Ok, this is fine but where to you actually make use of them?

You have some freedom in this.

The basic idea is that you can make use of the getArguments method to retrieve the arguments Bundle anywhere in your Fragment that they are needed. Of course it only makes sense to do this in methods that are called when the Fragment is being created or recreated.

In most cases this means retrieving the arguments in the Fragment's onCreate event handler or in the onCreateView handler.

The onCreate method is called before the onCreateView handler and it is most often used to retrieve the arguments and store them in locations that the rest of the Fragment can access. It also has the advantage that it keeps the onCreateView method dedicated to what it is supposed to do i.e. create the view. 

The generated onCreate simply checks to see if there are any arguments to retrieve and if there are it retrieves them and stores them in the private variables:

@Override
public void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 if (getArguments() != null) {
  mParam1 = getArguments().getString(ARG_PARAM1);
  mParam2 = getArguments().getString(ARG_PARAM2);
 }
}

Following this the arguments that you have passed are accessible via the private fields from anywhere within the Fragment class. 

if you are following this description it should be clear to you why the template doesn't just store the arguments into the private fields using the factory method or the parametrized constructor. 

So to summarise:

  1. Use the factory method or the parametrized constructor to store any arguments needed in the arguments Bundle
  2. Retrieve the arguments stored in the Bundle as part of the onCreate or if you really need to the onCreateView event handlers.

If you follow these two rules your Fragment will be correctly initialized when you create it and when the system recreates it. 

The final thing we have to do is look at the onCreateView event handler. This simply inflates the XML layout file and returns it as a result. In a more general situation it might use mParam1 or mParam2 to modify the UI in some way:

@Override
public View onCreateView(LayoutInflater inflater, 
                    ViewGroup container,
                    Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(
   R.layout.fragment_blank, container, false);
}

 

Androidgears

A Short Example

For an example of changing the UI via the parameters add a Button and a EditText object to the Fragment and we can then set their text fields to whatever is passed in by the factory method:

@Override
public View onCreateView(
                      LayoutInflater inflater, 
                      ViewGroup container,
                      Bundle savedInstanceState) {
 View v= inflater.inflate(R.layout.fragment_blank,
                                container, false);
 Button bt=(Button) v.findViewById(R.id.button);
 bt.setText(mParam1);
 EditText et=(EditText)
                   v.findViewById(R.id.editText);
 et.setText(mParam2);
 return v;
}

If you now create the Fragment in the Activity's OnCreate using:

 

if (savedInstanceState == null) {
 BlankFragment bf=BlankFragment.newInstance(
                          "String1","String2");
 getSupportFragmentManager()
  .beginTransaction()
  .add(R.id.container,bf)
  .commit();
}

(This code goes anywhere after the setContentView call).

You also need to remember to assign the id container to Activity's layout, i.e. @+id/container.

Also notice that we are using the support library because that is what the template does - hence getSupportFragmentManager. 



Last Updated ( Saturday, 25 July 2015 )