Module 0264: Android Fragments

Tak Auyeung, Ph.D.

February 8, 2017

1 About this module

2 Reusable user interface chunks

A Fragment is a class that is, essentially, a container of some user interface components. Unlike an Activity, a Fragment can only be a part of an Activity, but it can be a part of multiple Activity objects.

This means that Fragments are very useful as reusable modules as far as the user interface is concerned.

3 How to make one

In Android Studio, right click on the “app” in the Project pane, select “new”, then choose “fragment”. Android Studio will prompt for the name of the Fragment, the name of the layout for this fragment, and target source set.

Open the fragment layout, it should look almost the same as an activity. You can then insert and place user interface components in the fragment.

4 Fragment coding

Android Studio automatically generates a class associated with each fragment. Here is a quick explanation of the included code.

Android Studio can optionally (by default enabled) include “factory” code so that the constructor of a Fragment can be parametrized. This is useful because a fragment is a reusable component and it can be utilized in a variety of activities.

By default, Android Studio includes two String parameters with the generic names of “param1” and “param2”. These parameters are remembered as a Bundle via the Fragment method setArguments. However, these parameters are also stored as private members “mParam1” and “mParam2”. The private members are initialized in the onCreate listener.

5 Attaching a fragment to an activity layout

Once a fragment is created, it can be attached via layout editing to an activity. To do this in the layout designer, drag a “¡fragment¿” (as a part of “Layouts”) into the activity.

Android Studio then prompts the available subclasses of Fragment that is available. You can select one that is supplied by Android, or one that you just created. This lets you select the class associated with the fragment, but not the layout. You will need to select the layout separately.

Note that no code is generated to associate a fragment with an activity at this point!

If you try to run the app at this point, it will crash. The crash happens because there is no link between the fragment and the activity.

6 Linking a fragment to an activity in code

First, the class of the containing activity must implement the nested OnFragmentInteractionListener interface of the included fragment. This, in return, requires the definition of the onFragmentInteraction method as defined by the interface.

With the stub method defined, the app will run but without much functionality unless logic is attached. Should the logic be attached to the fragment class or the activity class?

Regardless of where business logic is attached, a fragment class is the first one to specify listeners to events related to the UI components. This means that if a fragment includes a button, the onClick listener of the button should be set up by the fragment class.

The real question is whether to let the fragment class handle the logic of responding to the click event, or to relay it to the containing activity to do this. If the choice is the latter, then the nested interface definition of the fragment should specify a method to handle the click event of the button so that whatever activity class implements this interface is guaranteed to have a corresponding method.

7 Back to MVA

In module 0259, we explored the concept of connecting model and view via adapters. This is a good opportunity to put that design pattern to use.

The real question is whether the view (in MVA) should be the activity containing the fragment, or the fragment itself. If a model is linked to a fragment, it means the associated adapter will also work regardless of which activity is including this fragment, making coding more modular. However, this approach also means the activity has no opportunity to insert any additional logic that is specific to the activity.

Depending on the coding of a fragment class, it is possible to make a model and the containing activity both be listeners of events from the fragment. This is done by using a List<OnFragmentInteraction> in a fragment class to track a list of listeners. This gives the activity and the model both a chance to do some processing.

8 Default fragment code

The default fragment code does not link any actual events to the automatically generated onButtonPressed or in any way call back the onFragmentInteraction method of mListener. Furthermore, the default onFragmentInteraction method takes an Uri parameter. What is going here?

The stub code generated by Android Studio presents “reminders” of what needs to be done. This way, any changes to the layout (of the fragment) do not make further automated changes to the code of the associated fragment class.

It is worthwhile to explain the Uri class in this context. Normally, we consider an URI a way to designate a web page. However, the “U” means universal, and as such it can be used to convey data or action of just about any kind.

As a result, all events from a fragment can go through the same listener method, as opposed to each event of each UI component having its own listener method.

You can change the code generated by Android Studio in any way that is consistent with the rest of your app, including the editing of the interface definition, removal or addition of additional methods to handle interaction with the UI components.

9 Finding a fragment attached to an activity via layout

From a method of an activity, getFragmentManager() or getSupportFragmentManager() can be called to get the fragment manager of the activity. The choice boils down to whether the app targets API 25+ (to use getFragmentManager()) or API ¡25 (to use getSupportFragmentManager()) This object, in return, can locate fragments that are attached to the activity by the ID of the fragment using findFragmentById. If the fragment is not attached, the method returns null.

If there is no intention to modularize the logic and code associated with a fragment, accessing the individual UI components of a fragment let an activity specify how events are handled. From the perspective of an activity class, this is done by code similar to the following:

    View uiComponent = getFragmentManager.findFragmentById(R.id.fragmentX).getView().findViewById(R.id.someChildViewId);

In this code fragmentX is the ID of the fragment from the perspective of the containing activity, and someChildViewId is the ID of the user interaction component View object from the perspective of the fragment.

Once uiComponent has a reference to a View object, we can then use the setOn...Listener methods to specify code to run in response to events that are specific to the UI component.

Using a fragment in this manner provides the maximum amount of flexibility to the containing activity, but it also minimizes the modularity of code associated with a fragment.

10 Specifying event handlers in a fragment

Inside a fragment class, UI components (View objects) can be retrieved by first retrieving the overall view of the fragment using getView(), then use findViewById based on layout IDs of the included components.

The point to specify handlers, however, is a little different from that of an activity. For activities, handlers can be attached in the overriden onCreate callback. For fragments, handlers can only be attached in an overriden onCreateView callback.

This is because the onCreate call of an activity is where the UI is inflated (objects created based on layout XML file specification). However, in the case of a fragment, UI inflation is performed in the onCreateView callback (this code is generated by Android Studio).

The onCreate callback of a fragment is for the initialization of the internal state of a fragment object, but not for the layout to be inflated.

It should also be noted that both onCreate and onCreateView of a fragment are indirectly called from onCreate of the containing activity. Both fragment methods are indirectly called from the call to setContentView from the activity onCreate method.