Using a Fragment

Now that we have our customized fragment complete with a very simple UI it is time to display it in an Activity.

This is perhaps the most complicated part of using a Fragment although once you get used to it its not so difficult.

The first new feature is that every Activity has a FragmentManager which is used to control how Fragments are displayed. To display a Fragment you first have to obtain the FragmentManager using getFragmentManager. Then you have to get a FragmentTransaction from the FragmentManager.

You can't do anything with a Fragment unless you start a transaction. Within the transaction you can set up what you want to happen, usually add the Fragment to the current layout, but nothing happens until you use the commit method.

The final complication is that the add method, used to add a Fragment to a specified container needs the container specified by id number. This fine for an XML layout but not so convenient for a code generated layout.

That is if you have a FragmentTransaction ft then:

ft.add(id,frag1);

adds Fragment frag1 to the ViewGroup with the specified id. Notice this is not a Layout Id.

Lets see how to implement all of this.

Let's add the Fragment to the MainActivity using the usual onCreate method:

The View hierarchy after the Fragment has been added consists of a single LinearLayout created by the Activity, containing a LinearLayout created by the Fragment, which contains a Button created by the Fragment.

There are some subtle points to note that aren't important at the moment, but could come back to bite you when you try to do some more advanced things.

The first is that the Fragment's View hierarchy is not constructed at the end of the transaction, i.e. immediately after the commit. The onCreateView event is fired some time after the onCreate event handler returns. This means you cannot access the View objects added by the Fragment within the onCreate method.

Secondly, at the moment we are creating the Fragment each time the onCreate method is fired by the Activity's Create event but the system automatically saves and restores Fragments when the Activity is suspended. In particular the entire Activity and its associated Fragments are destroyed if a configuration change such as a orientation change occurs. In this case the system recreates the Fragment and the Activity for you.

You need to keep this in mind because this automatic destroy- recreate mechanism causes most of the complications you will encounter in using Fragments.

The current onCreate method works, but it is inefficient because it manually creates the Fragment after the system has restored the old instance.

By checking to see if there is any saved instance state we can avoid creating the Fragment when it isn't needed.

Test that this works by running the app and then rotating the device.

IMPORTANT NOTE:If you find that the rotation test doesn't work you are probably testing using 4.4.x (API19) in which the emulator has a bug which means it doesn't respond to rotation commands. The solution is to downgrade to 4.3 (API18) until the problem is solved.

We need to look more at how Fragments can retain their state when the system destroys and recreates them.

By now you might well be thinking that this is a lot of work to go to when you could have just added the Button to the layout in the Activity. This is true enough and the reason that Fragments and their use have been left until now. Android Studio does have a way of making all of this seem much easier but at some point you do have to understand what it is doing to get any value from using Fragments.

It is time to move on and look at how to make Fragments do something.

Fragment Events

The next topic we have to consider is the issue of how much work a Fragment should do.

For example, when a button is clicked what should handle the event?

The idea of a Fragment is that it is a wrapped up chunk of UI that can be used by any Activity that needs it.

On this basis you have to ask yourself if the event that has to be handled is the same for every Activity or unique to each Activity. If it is the same then the event handler is better written within the Fragment.

Let's see how to do this.

The principle of getting a Fragment to handle an event is easy enough to understand. The Fragment doesn't have a UI of its own - it is displayed by an Activity that the Fragment is associated with. The events are generated by objects in the View hierarchy, which is owned by the Activity. If you try to use Android Studio to add an event handler, for example, it will add it to the Activity and not to the Fragment.

You can, however, define the EventListener that you want to handle the event in the Fragment and then hook it up to the View object in the Activity in which you want to generate the event.

Creating the event handler follows the same two possible patterns that you used to create it in the Activity.

Starting in the Fragment you can either make it implement the necessary interface or you can use an anonymous class. The anonymous class approach is the more flexible so let's do it this way.

First let's add a TextView to be used in the event handler. The user can click the button and transfer the button's text to the TextView. Add the following code to the onCreateView method:

If you recall Java uses objects to handle events. You create an object of the correct type using an anonymous class which avoids having to create a named class in a separate file.

In this case the OnClickListener has just one method the onClick event handler that we have to override. It is assumed that the event handler will only be attached to a Button so we are safe in casting the view parameter to a Button object. Next we can use the button text to set the TextView's text.

At this point you might be worried by the use of the local variable tv as it doesn't belong to the OnClickListener object but the containing Fragment. How can this class access a variable that isn't part of it?