7
UI Fragments and the Fragment
Manager
In this chapter, you will start building an application named CriminalIntent. CriminalIntent records the
details of “office crimes” – things like leaving dirty dishes in the breakroom sink or walking away from
an empty shared printer after documents have printed.
With CriminalIntent, you can make a record of a crime including a title, a date, and a photo. You
can also identify a suspect from your contacts and lodge a complaint via email, Twitter, Facebook,
or another app. After documenting and reporting a crime, you can proceed with your work free of
resentment and ready to focus on the business at hand.
CriminalIntent is a complex app that will take 13 chapters to complete. It will have a list-detail
interface: The main screen will display a list of recorded crimes, and users will be able to add new
crimes or select an existing crime to view and edit its details (Figure 7.1).
Figure 7.1 CriminalIntent, a list-detail app
123
Chapter 7 UI Fragments and the Fragment Manager
The Need for UI Flexibility
You might imagine that a list-detail application consists of two activities: one managing the list and the
other managing the detail view. Clicking a crime in the list would start an instance of the detail activity.
Pressing the Back button would destroy the detail activity and return you to the list, where you could
select another crime.
That would work, but what if you wanted more sophisticated presentation and navigation between
screens?
• Imagine that your user is running CriminalIntent on a tablet. Tablets and some larger phones have
screens large enough to show the list and detail at the same time – at least in landscape orientation
(Figure 7.2).
Figure 7.2 Ideal list-detail interface for phone and tablet
• Imagine the user is viewing a crime on a phone and wants to see the next crime in the list. It
would be better if the user could swipe to see the next crime without having to return to the list.
Each swipe should update the detail view with information for the next crime.
What these scenarios have in common is UI flexibility: the ability to compose and recompose an
activity’s view at runtime depending on what the user or the device requires.
Activities were not built to provide this flexibility. An activity’s views may change at runtime, but the
code to control those views must live inside the activity. As a result, activities are tightly coupled to the
particular screen being used.
124
Introducing Fragments
Introducing Fragments
You can get around the letter of the Android law by moving the app’s UI management from the activity
to one or more fragments.
A fragment is a controller object that an activity can deputize to perform tasks. Most commonly, the
task is managing a UI. The UI can be an entire screen or just one part of the screen.
A fragment managing a UI is known as a UI fragment. A UI fragment has a view of its own that is
inflated from a layout file. The fragment’s view contains the interesting UI elements that the user wants
to see and interact with.
The activity’s view contains a spot where the fragment’s view will be inserted. In fact, while in this
chapter the activity will host a single fragment, an activity can have several spots for the views of
several fragments.
You can use the fragment(s) associated with the activity to compose and recompose the screen as your
app and users require. The activity’s view technically stays the same throughout its lifetime, and no
laws of Android are violated.
Let’s see how this would work in a list-detail application to display the list and detail together. You
would compose the activity’s view from a list fragment and a detail fragment. The detail view would
show the details of the selected list item.
Selecting another item should display a new detail view. This is easy with fragments; the activity will
replace the detail fragment with another detail fragment (Figure 7.3). No activities need to die for this
major view change to happen.
Figure 7.3 Detail fragment is swapped out
Using UI fragments separates the UI of your app into building blocks, which is useful for more than
just list-detail applications. Working with individual blocks, it is easy to build tab interfaces, tack on
animated sidebars, and more.
Achieving this UI flexibility comes at a cost: more complexity, more moving parts, and more code.
You will reap the benefits of using fragments in Chapter 11 and Chapter 17. The complexity, however,
starts now.
125
Chapter 7 UI Fragments and the Fragment Manager
Starting CriminalIntent
In this chapter, you are going to start on the detail part of CriminalIntent. Figure 7.4 shows you what
CriminalIntent will look like at the end of this chapter.
Figure 7.4 CriminalIntent at the end of this chapter
The screen shown in Figure 7.4 will be managed by a UI fragment named CrimeFragment. An instance
of CrimeFragment will be hosted by an activity named CrimeActivity.
126
Starting CriminalIntent
For now, think of hosting as the activity providing a spot in its view hierarchy where the fragment can
place its view (Figure 7.5). A fragment is incapable of getting a view on screen itself. Only when it is
placed in an activity’s hierarchy will its view appear.
Figure 7.5 CrimeActivity hosting a CrimeFragment
127
Chapter 7 UI Fragments and the Fragment Manager
CriminalIntent will be a large project, and one way to keep your head wrapped around a project is with
an object diagram. Figure 7.6 gives you the big picture of CriminalIntent. You do not have to memorize
these objects and their relationships, but it is good to have an idea of where you are heading before you
start.
You can see that CrimeFragment will do the sort of work that your activities did in GeoQuiz: create and
manage the UI and interact with the model objects.
Figure 7.6 Object diagram for CriminalIntent (for this chapter)
Three of the classes shown in Figure 7.6 are classes that you will write: Crime, CrimeFragment, and
CrimeActivity.
An instance of Crime will represent a single office crime. In this chapter, a crime will have a title, an
ID, a date, and a boolean that indicates whether the crime has been solved. The title is a descriptive
name, like “Toxic sink dump” or “Someone stole my yogurt!” The ID will uniquely identify an
instance of Crime.
For this chapter, you will keep things very simple and use a single instance of Crime. CrimeFragment
will have a member variable (mCrime) to hold this isolated incident.
CrimeActivity’s view will consist of a FrameLayout that defines the spot where the CrimeFragment’s
view will appear.
CrimeFragment’s view will consist of a LinearLayout with a few child views inside of it, including
an EditText, a Button, and a CheckBox. CrimeFragment will have member variables for each of these
views and will set listeners on them to update the model layer when there are changes.
128
Creating a new project
Creating a new project
Enough talk; time to build a new app. Create a new Android application (File → New Project...). Name
the application CriminalIntent and make sure the company domain is [Link], as
shown in Figure 7.7.
Figure 7.7 Creating the CriminalIntent application
Click Next and specify a minimum SDK of API 19: Android 4.4. Also ensure that only the Phone and
Tablet application type is checked.
Click Next again to select the type of activity to add. Choose Empty Activity and continue along in the
wizard.
129
Chapter 7 UI Fragments and the Fragment Manager
In the final step of the New Project wizard, name the activity CrimeActivity and click Finish
(Figure 7.8).
Figure 7.8 Creating CrimeActivity
Two types of fragments
Fragments were introduced in API level 11 along with the first Android tablets and the sudden need
for UI flexibility. You must choose which implementation of fragments that you want use: native
fragments or support fragments.
The native implementation of fragments is built into the device that the user runs your app on. If you
support many different versions of Android, each of those Android versions could have a slightly
different implementation of fragments (for example, a bug could be fixed in one version and not
the versions prior to it). The support implementation of fragments is built into a library that you
include in your application. This means that each device you run your app on will depend on the same
implementation of fragments no matter the Android version.
In CriminalIntent, you will use the support implementation of fragments. Detailed reasoning for this
decision is laid out at the end of the chapter in the section called For the More Curious: Why Support
Fragments Are Superior.
130
Adding dependencies in Android Studio
Adding dependencies in Android Studio
You will use the implementation of fragments that comes with the AppCompat library. The
AppCompat library is one of Google’s many compatibility libraries that you will use throughout this
book. You will learn much more about the AppCompat library in Chapter 13.
To use the AppCompat library, it must be included in your list of dependencies. Your project comes
with two [Link] files, one for the project as a whole and one for your app module. Open the
[Link] file located in your app module.
Listing 7.1 Gradle dependencies (app/[Link])
apply plugin: '[Link]'
android {
...
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
...
compile '[Link]:appcompat-v7:25.0.1'
...
}
In the current dependencies section of your [Link] file, you should see something similar to
Listing 7.1 that specifies that the project depends on all of the .jar files in its libs directory. You will
also see dependencies for other libraries that are automatically included when projects are created with
Android Studio, most likely including the AppCompat library.
Gradle allows for the specification of dependencies that you have not copied into your project. When
your app is compiled, Gradle will find, download, and include the dependencies for you. All you have
to do is specify an exact string incantation and Gradle will do the rest.
If you do not have the AppCompat library listed in your dependencies, Android Studio has a tool to
help you add the library and come up with this string incantation. Navigate to the project structure for
your project (File → Project Structure...).
131
Chapter 7 UI Fragments and the Fragment Manager
Select the app module on the left and the Dependencies tab in the app module. The dependencies for
the app module are listed here (Figure 7.9).
Figure 7.9 App dependencies
(You may have additional dependencies specified. If you do, do not remove them.)
You should see the AppCompat dependency listed. If you do not, add it with the + button and choose
Library dependency. Choose the appcompat-v7 library from the list and click OK (Figure 7.10).
Figure 7.10 A collection of dependencies
132
Adding dependencies in Android Studio
Navigate back to the editor window showing app/[Link], and you should now see AppCompat
included, as shown in Listing 7.1.
(If you modify this file manually, outside of the project structure window, you will need to sync your
project with the Gradle file to reflect any updates that you have made. This sync asks Gradle to update
the build based on your changes by either downloading or removing dependencies. Changes within the
project structure window will trigger this sync automatically. To manually perform this sync, navigate
to Tools → Android → Sync Project with Gradle Files.)
The dependency string compile '[Link]:appcompat-v7:25.0.0' uses the Maven
coordinates format groupId:artifactId:version. (Maven is a dependency management tool. You can
learn more about it at [Link]/.)
The groupId is the unique identifier for a set of libraries available on the Maven repository. Often
the library’s base package name is used as the groupId, which is [Link] for the
AppCompat library.
The artifactId is the name of a specific library within the package. In this case, the name of the
library you are referring to is appcompat-v7.
Last but not least, the version represents the revision number of the library. CriminalIntent depends
on the 25.0.0 version of the appcompat-v7 library. Version 25.0.0 is the latest version as of this writing,
but any version newer than that should also work for this project. In fact, it is a good idea to use the
latest version of the support library so that you can use newer APIs and receive the latest bug fixes. If
Android Studio added a newer version of the library for you, do not roll it back to the version shown
above.
Now that the AppCompat library is a dependency in the project, make sure that your project uses it. In
the project tool window, find and open [Link]. Verify that CrimeActivity’s superclass
is AppCompatActivity.
Listing 7.2 Tweaking template code ([Link])
public class CrimeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
[Link](savedInstanceState);
setContentView([Link].activity_crime);
}
Before proceeding with CrimeActivity, let’s create the model layer for CriminalIntent by writing the
Crime class.
133
Chapter 7 UI Fragments and the Fragment Manager
Creating the Crime class
In the project tool window, right-click the [Link] package and
select New → Java Class. Name the class Crime and click OK.
In [Link], add fields to represent the crime’s ID, title, date, and status and a constructor that
initializes the ID and date fields (Listing 7.3).
Listing 7.3 Adding to Crime class ([Link])
public class Crime {
private UUID mId;
private String mTitle;
private Date mDate;
private boolean mSolved;
public Crime() {
mId = [Link]();
mDate = new Date();
}
}
UUID is a Java utility class included in the Android framework. It provides an easy way to generate
universally unique ID values. In the constructor you generate a random unique ID by calling
[Link]().
Android Studio may find two classes with the name Date. Use the Option+Return (or Alt+Enter)
shortcut to manually import the class. When asked which version of the Date class to import, choose
the [Link] version.
Initializing the Date variable using the default Date constructor sets mDate to the current date. This will
be the default date for a crime.
134
Creating the Crime class
Next, you want to generate a getter for the read-only mId and both a getter and setter for mTitle,
mDate, and mSolved. Right-click after the constructor and select Generate... → Getter and select the
mId variable. Then, generate the getter and setter for mTitle, mDate, and mSolved by repeating the
process, but selecting Getter and Setter in the Generate... menu.
Listing 7.4 Generated getters and setters ([Link])
public class Crime {
private UUID mId;
private String mTitle;
private Date mDate;
private boolean mSolved;
public Crime() {
mId = [Link]();
mDate = new Date();
}
public UUID getId() {
return mId;
}
public String getTitle() {
return mTitle;
}
public void setTitle(String title) {
mTitle = title;
}
public Date getDate() {
return mDate;
}
public void setDate(Date date) {
mDate = date;
}
public boolean isSolved() {
return mSolved;
}
public void setSolved(boolean solved) {
mSolved = solved;
}
}
That is all you need for the Crime class and for CriminalIntent’s model layer in this chapter.
At this point, you have created the model layer and an activity that is capable of hosting a support
fragment. Now you will get into the details of how the activity performs its duties as host.
135
Chapter 7 UI Fragments and the Fragment Manager
Hosting a UI Fragment
To host a UI fragment, an activity must:
• define a spot in its layout for the fragment’s view
• manage the lifecycle of the fragment instance
The fragment lifecycle
Figure 7.11 shows the fragment lifecycle. It is similar to the activity lifecycle: It has stopped, paused,
and resumed states, and it has methods you can override to get things done at critical points – many of
which correspond to activity lifecycle methods.
Figure 7.11 Fragment lifecycle diagram
The correspondence is important. Because a fragment works on behalf of an activity, its state should
reflect the activity’s state. Thus, it needs corresponding lifecycle methods to handle the activity’s work.
One critical difference between the fragment lifecycle and the activity lifecycle is that fragment
lifecycle methods are called by the hosting activity, not the OS. The OS knows nothing about the
fragments that an activity is using to manage things. Fragments are the activity’s internal business.
You will see more of the fragment lifecycle methods as you continue building CriminalIntent.
136
Two approaches to hosting
Two approaches to hosting
You have two options when it comes to hosting a UI fragment in an activity:
• add the fragment to the activity’s layout
• add the fragment in the activity’s code
The first approach is known as using a layout fragment. It is straightforward but inflexible. If you add
the fragment to the activity’s layout, you hardwire the fragment and its view to the activity’s view and
cannot swap out that fragment during the activity’s lifetime.
The second approach, adding the fragment to the activity’s code, is more complex – but it is the only
way to have control at runtime over your fragments. You determine when the fragment is added to the
activity and what happens to it after that. You can remove the fragment, replace it with another, and
then add the first fragment back again.
Thus, to achieve real UI flexibility you must add your fragment in code. This is the approach you will
use for CrimeActivity’s hosting of a CrimeFragment. The code details will come later in the chapter.
First, you are going to define CrimeActivity’s layout.
Defining a container view
You will be adding a UI fragment in the hosting activity’s code, but you still need to make a spot for
the fragment’s view in the activity’s view hierarchy. In CrimeActivity’s layout, this spot will be the
FrameLayout shown in Figure 7.12.
Figure 7.12 Fragment-hosting layout for CrimeActivity
This FrameLayout will be the container view for a CrimeFragment. Notice that the container view is
completely generic; it does not name the CrimeFragment class. You can and will use this same layout
to host other fragments.
Locate CrimeActivity’s layout at res/layout/activity_crime.xml. Open this file and replace the
default layout with the FrameLayout diagrammed in Figure 7.12. Your XML should match Listing 7.5.
Listing 7.5 Creating the fragment container layout (activity_crime.xml)
<FrameLayout
xmlns:android="[Link]
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
137
Chapter 7 UI Fragments and the Fragment Manager
Note that while activity_crime.xml consists solely of a container view for a single fragment, an
activity’s layout can be more complex and define multiple container views as well as widgets of its
own.
You can preview your layout file or run CriminalIntent to check your code. You will see an empty
FrameLayout below a toolbar containing the text CriminalIntent (Figure 7.13). (If the preview window
does not render the screen correctly, or you see errors, build the project by selecting Build → Rebuild
Project. If that still does not work correctly, run the app on your emulator or device. As of this writing,
the preview window can be finicky.)
Figure 7.13 An empty FrameLayout
The FrameLayout is empty because the CrimeActivity is not yet hosting a fragment. Later, you
will write code that puts a fragment’s view inside this FrameLayout. But first, you need to create a
fragment.
(The toolbar at the top of your app is included automatically because of the way you configured your
activity. You will learn more about the toolbar in Chapter 13.)
138
Creating a UI Fragment
Creating a UI Fragment
The steps to create a UI fragment are the same as those you followed to create an activity:
• compose a UI by defining widgets in a layout file
• create the class and set its view to be the layout that you defined
• wire up the widgets inflated from the layout in code
Defining CrimeFragment’s layout
CrimeFragment’s view will display the information contained within an instance of Crime.
First, define the strings that the user will see in res/values/[Link].
Listing 7.6 Adding strings (res/values/[Link])
<resources>
<string name="app_name">CriminalIntent</string>
<string name="crime_title_hint">Enter a title for the crime.</string>
<string name="crime_title_label">Title</string>
<string name="crime_details_label">Details</string>
<string name="crime_solved_label">Solved</string>
</resources>
Next, you will define the UI. The layout for CrimeFragment will consist of a vertical LinearLayout
that contains two TextViews, an EditText, a Button, and a Checkbox.
To create a layout file, right-click the res/layout folder in the project tool window and select New →
Layout resource file. Name this file fragment_crime.xml and enter LinearLayout as the root element.
Click OK and Android Studio will generate the file for you.
139
Chapter 7 UI Fragments and the Fragment Manager
When the file opens, navigate to the XML. The wizard has added the LinearLayout for you. Add the
widgets that make up the fragment’s layout to fragment_crime.xml.
Listing 7.7 Layout file for fragment’s view (fragment_crime.xml)
<LinearLayout xmlns:android="[Link]
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:orientation="vertical">
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_title_label"/>
<EditText
android:id="@+id/crime_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/crime_title_hint"/>
<TextView
style="?android:listSeparatorTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_details_label"/>
<Button
android:id="@+id/crime_date"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<CheckBox
android:id="@+id/crime_solved"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/crime_solved_label"/>
</LinearLayout>
140
Defining CrimeFragment’s layout
Check the Design view to see a preview of your fragment’s view (Figure 7.14).
Figure 7.14 Previewing updated crime fragment layout
(The updated fragment_crime.xml code includes new syntax related to view style: <style="?
android:listSeparatorTextViewStyle". Fear not. You will learn the meaning behind this syntax in
the section called Styles, themes, and theme attributes in Chapter 9.)
141
Chapter 7 UI Fragments and the Fragment Manager
Creating the CrimeFragment class
Right-click the [Link] package and select New → Java Class.
Name the class CrimeFragment and click OK to generate the class.
Now, turn this class into a fragment. Update CrimeFragment to subclass the Fragment class.
Listing 7.8 Subclassing the Fragment class ([Link])
public class CrimeFragment extends Fragment {
As you subclass the Fragment class, you will notice that Android Studio finds two classes with the
Fragment name. You will see Fragment ([Link]) and Fragment ([Link]).
The [Link] Fragment is the version of fragments built into the Android OS. You will use the
support library version, so be sure to select the [Link] version of the Fragment class
when you see the dialog, as shown in Figure 7.15.
Figure 7.15 Choosing the support library’s Fragment class
Your code should match Listing 7.9.
Listing 7.9 Supporting the Fragment import ([Link])
package [Link];
import [Link];
public class CrimeFragment extends Fragment {
If you do not see this dialog or the wrong fragment class was imported, you can manually import the
correct class. If you have an import for [Link], remove that line of code. Import the
correct Fragment class with the Option+Return (or Alt+Enter) shortcut. Be sure to select the support
version of the Fragment class.
142