Advertisement
Android SDK

Android User Interface Design: Working With Fragments

by

The new Fragment API for Android, introduced in Android 3.0, allows for easier dynamic user interfaces. In this tutorial, learn how to convert a two-screen ListView to WebView workflow into a single screen workflow designed for large screens, such as those found on tablets.

Subsequent Changes to Techniques & Software

Certain aspects of applications or techniques used in this tutorial have changed since it was originally published. This might make it a little difficult to follow along. We'd recommend looking at these more recent tutorials on the same topic:

The pacing of this tutorial is going to be faster than some of our beginner tutorials; you may have to review some of the other Android tutorials on this site or even in the Android SDK reference if you are unfamiliar with any of the basic Android concepts and classes discussed in this tutorial. The final sample code that accompanies this tutorial is available for download as open-source from the Google code hosting.

Introducing Fragments

Before we get started, let's define what a Fragment is, at a high level. A Fragment is, generally, a chunk of user interface with its own life cycle. If that sounds a lot like an Activity, that's because it is a lot like an Activity. However, a Fragment is different from an Activity, in that a Fragment must exist within an Activity. A Fragment doesn't have to be paired with the same Activity each time it's instantiated, which gives it some flexibility. Also like an Activity, a Fragment need not contain any user interface.

Step 0: Getting Started

This tutorial assumes you will start where our ListView tutorial left off. You can download that code and build from there, though you will have some tasks you'll have to do unassisted, or you can download the code for this tutorial and follow along.

Step 1: Redesigning the Screens

The following figure illustrates the existing workflow of our Mobiletuts+ article reader app (the ListView tutorial) before a Fragment design was considered and applied:

Android SDK Fragments - Figure 1

This workflow works fine on a relatively small phone screen. However, on a large screen, like the 10" screen of the Motorola Xoom, and there's a lot of wasted space on the ListView screen. The WebView screen looks fine, if a bit boring.

This is where Fragments come into play: on larger screens, we could provide a more effective user interface if we could display the ListView on the same screen as the WebView. When the user clicks a specific ListView element in the lefthand “pane”, the WebView on the right-hand side updates to display the appropriate content. This type of workflow is frequently used in email or document feed readers. The following figure illustrates just such a redesign:

Android SDK Fragments - Figure 2

Step 2: Converting to a Fragments-Based Design

Now that we know how the new screen workflow will be designed, we also know that the two current activities will need to be converted to fragments. We'll do the conversion in several steps. The first step involves leaving the screens visually unchanged, but modifying each screen to use a fragment. One fragment will contain the current ListView and another will contain the WebView. Then we'll switch to a single screen implementation, which involves modifying the messaging between the ListView and WebView activities-turned-fragments.

First, though, change the Project Build Target of your application to Android 3.0. To do this from within Eclipse, right-click on the project and choose Properties. Navigate to the Android section and check the checkbox next to Android 3.0. We're not using any Google APIs, so the Android Open Source Project version is sufficient. Then click the OK button.

Now you'll have access to the new APIs, including the Fragments API.

Note: In a future tutorial, we'll talk about using the new compatibility layer to enable technologies like the Fragment API to work on earlier versions of Android. For now, though, they will require a device with Android 3.0, Honeycomb.

Step 3: Creating the Fragment Classes

Create two new Java classes to represent the two fragments: the ListView and the WebView screens. Name them TutListFragment and TutViewerFragment. TutListFragment will extend the ListFragment class and TutViewerFragment will just extend the Fragment class.

Within the TutListFragment class, we need to override two methods: onListItemClick() and onCreate(). The contents of these methods should look familiar; they match what we previously had in the TutListActivity class. This will change shortly, but not just yet. Here’s a listing of the TutListFragment class, for now:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    String[] links = getResources().getStringArray(R.array.tut_links);

    String content = links[position];
    Intent showContent = new Intent(getActivity().getApplicationContext(),
            TutViewerActivity.class);
    showContent.setData(Uri.parse(content));
    startActivity(showContent);
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setListAdapter(ArrayAdapter.createFromResource(getActivity()
            .getApplicationContext(), R.array.tut_titles,
            R.layout.list_item));
}

The TutViewerFragment class is a little simpler. We use the fact that we know (for now) that the fragment is running under the same activity it used to and grab the intent data directly from within the Fragment class. Add an override method for the onCreateView() method. This methodshould now look like this:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Intent launchingIntent = getActivity().getIntent();
    String content = launchingIntent.getData().toString();

    WebView viewer = (WebView) inflater.inflate(R.layout.tut_view, container, false);
    viewer.loadUrl(content);
    
    return viewer;
}

The ability to access the activity instance directly is useful, but will create a problem later on. What if this fragment is on the screen with the list fragment? In that scenario, there will be no launching Intent to get the URL from. Similarly, in TutListFragment, we launch a new Activity directly whenever the user clicks an item in the list. What if the TutViewFragment existed within the same activity? If so, launching a new activity would make no sense.We'll return to resolve these issues later in this tutorial.

Step 4: Adding the Fragment Layout Resources

Now create a new layout file called tutlist_fragment.xml to represent the Fragment containing the list of articles. A Fragment layout resource uses the tag and references the Fragment class you created.

<?xml version="1.0" encoding="utf-8"?>
<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.mamlambo.tutorial.tutlist.TutListFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/tutlist_fragment">
</fragment>

Next, create a similar layout file called tutview_fragment.xml:

<?xml version="1.0" encoding="utf-8"?>
<fragment
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:name="com.mamlambo.tutorial.tutlist.TutViewerFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/tutview_fragment">
</fragment>

Step 5: Updating the Activity Classes

The TutListActivity and TutViewerActivity classes must now be updated. The TutListActivity class has a single method, onCreate(), which should now be updated to load the appropriate Fragment layout resource you created in the previous step, like this:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.tutlist_fragment);
}

Also, of note, the TutListActivity should inherit from the Activity class, not ListActivity.

The TutViewerActivity class requires a similar change. Its onCreate() method should now look like this:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.tutview_fragment);
}

Step 6: Checking Your Progress

Try running the application now. You'll note that it does exactly what it used to do. Not very exciting yet, is it? However, the entire user interface is now run using fragments. This will allow the next changes you need to make to go smoothly as we add a new layout to combine the two fragments for larger displays to show to the user on a single screen. However, as you may have noticed, the communication between fragments is handled identically to how we communicate between activities. In fact, we used the knowledge that the activity each fragment was paired with remained unchanged. This will not be the case when we have a single activity that contains and manages both fragments. Let's fix this first.

Step 7: Changing the Communication for TutListFragment

As you learned in Step 3, launching an activity directly from the TutListFragment object doesn't make sense any longer. The WebView UI may, in fact, be part of the same activity as the List—that’s our plan anyway for larger screens. In that case, we just want to update the URL of the WebView in the second fragment.

To make this change, we need to do several things. First, let's make the fragments independent of the activity in which they reside. To do this, add a listener interface to the TutListFragment class, as such:

public interface OnTutSelectedListener {
    public void onTutSelected(Uri tutUri);
}

And then trigger it by updating the onListItemClickListener() method as follows:

@Override
public void onListItemClick(ListView l, View v, int position, long id) {
    String[] links = getResources().getStringArray(R.array.tut_links);

    String content = links[position];
    tutSelectedListener.onTutSelected(Uri.parse(content));
}

Next, have the TutListActivity class implement the OnTutSelectedListener interface, like this:

public class TutListActivity extends Activity implements
        TutListFragment.OnTutSelectedListener {
...
@Override
public void onTutSelected(Uri tutUri) {
    Intent showContent = new Intent(getApplicationContext(),
            TutViewerActivity.class);
    showContent.setData(tutUri);
    startActivity(showContent);
}

So now we have the functionality split up between the fragment, which handles the user interface actions, and the activity, which can be a controller, passing the data on to the next activity. We'll modify the onTutSelected() method later to decide whether or not to launch a new Activity instance or update the existing fragment instance.

Step 8: Changing the Communication for TutViewerFragment

Now let’s move our attention to the TutViewerFragment class, which needs to be updated as well. Instead of querying the launch intent to find out which URL to load, the fragment will wait to be told what URL to load. In this way, we can update the WebView directly and not recreate the fragment with each load.

First, modify the TutViewerFragment class to contain a new method called updateUrl():

public void updateUrl(String newUrl) {
    if (viewer != null) {
        viewer.loadUrl(newUrl);
    }
}

Next, remove all functionality from the onCreateView() method, except the inflate() call. Over in the TutViewerActivity class, add the functionality back to retrieve the Intent and then call the updateUrl() method, like so:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.tutview_fragment);

    Intent launchingIntent = getIntent();
    String content = launchingIntent.getData().toString();

    TutViewerFragment viewer = (TutViewerFragment) getFragmentManager()
            .findFragmentById(R.id.tutview_fragment);
    
    viewer.updateUrl(content);
}

At this point, the application behavior remains unchanged. The fragments, however, can now exist within the same Activity or separate ones without further code changes.

Step 9: Adding a Dual Fragment Layout

Now let’s create a layout with both fragments, for use in certain situations. In the layout-land folder (which you may need to create), add a copy of tutlist_fragment.xml. This will provde a different layout for landscape orientation on any landscape screen. Portrait mode will remain unchanged. Edit the file to look like the following layout with both fragments:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <fragment
        android:name="com.mamlambo.tutorial.tutlist.TutListFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:id="@+id/tutlist_fragment"
        android:layout_weight="45">
    </fragment>
    <fragment
        android:name="com.mamlambo.tutorial.tutlist.TutViewerFragment"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:id="@+id/tutview_fragment"
        android:layout_weight="55">
    </fragment>
</LinearLayout>

This will divide the screen horizontally between both fragments.

Step 10: Adding a Dynamic Choice

Now we can add some simple logic to the application to choose between launching a new activity (the two screen workflow) and updating an existing fragment (the one screen workflow).

To do this, update the onTutSelected() method of the TutListActivity class as follows:

@Override
public void onTutSelected(String tutUrl) {
    TutViewerFragment viewer = (TutViewerFragment) getFragmentManager()
            .findFragmentById(R.id.tutview_fragment);

    if (viewer == null || !viewer.isInLayout()) {
        Intent showContent = new Intent(getApplicationContext(),
                TutViewerActivity.class);
        showContent.setData(Uri.parse(tutUrl));
        startActivity(showContent);
    } else {
        viewer.updateUrl(tutUrl);
    }
}

All this does is grab the fragment and check to see if it's part of the existing layout for the Activity. If not, the viewer activity is launched, otherwise the existing fragment is updated instead.

Step 11: Running the Newly Fragment-Aware App

At this point, the application will now function in two different modes: portrait is unchanged, while landscape displays the ListView to the left of the WebView. There are several improvements that could be made at this point, but they are of the tweaking, optimizing, and nit-picking variety and mostly for polish. For instance, if you are in portrait WebView mode and rotate the screen, the result is still just the WebView screen. You have to press back to get to the dual view. Polishing is beyond the scope of this tutorial, but you can see how, with the judicisious use of layouts and a little bit of activity logic, you can achieve powerful but flexible screen workflows for a variety of screens and devices.

Android SDK Fragments - Tutorial 3

Conclusion

The Fragment API helps organize user interface components so that they can be reused across activities. In this way, an application can dynamically adjust its workflow and user interfaces with relatively little coding overhead. You've also seen that an application that builds upon fragments is easier to reorganize. Even better, just about any application can leverage fragments now that they are available via a compatibility library provided by Google that is compatible as far back as Android 1.6.
Now go out and fragment your apps user interface and make awesome user interfaces for every screen size and shape!

About the Authors

Mobile developers Lauren Darcey and Shane Conder have coauthored several books on Android development: an in-depth programming book entitled Android Wireless Application Development and Sams Teach Yourself Android Application Development in 24 Hours. When not writing, they spend their time developing mobile software at their company and providing consulting services. They can be reached at via email to androidwirelessdev+mt@gmail.com, via their blog at androidbook.blogspot.com, and on Twitter @androidwireless.

Need More Help Writing Android Apps? Check out our Latest Books and Resources!

Buy Android Wireless Application Development, 2nd Edition  Buy Sam's Teach Yourself Android Application Development in 24 Hours  Mamlambo code at Code Canyon

Related Posts
  • Code
    Mobile Development
    Streaming Video in Android Apps54dpm preview image@2x
    The Android platform provides libraries you can use to stream media files, such as remote videos, presenting them for playback in your apps. In this tutorial, I will show you how to stream a video file using these libraries.Read More…
  • Code
    Android SDK
    Create a Music Player on Android: Project Setup0d63m preview image@2x
    The Android platform provides resources for handling media playback, which your apps can use to create an interface between the user and their music files. In this tutorial series, we will create a basic music player application for Android. The app will present a list of songs on the user device, so that the user can select songs to play. The app will also present controls for interacting with playback and will continue playing when the user moves away from the app, with a notification displayed while playback elapses.Read More…
  • Code
    Android SDK
    Using the Accelerometer on Android06lom preview image@2x
    In this tutorial, we are going to explore how to use the accelerometer, one of the many hardware sensors of modern smartphones, in an Android application. I'll explain what an accelerometer is and why it may be something you want to take advantage of in your Android applications.Read More…
  • Code
    Android SDK
    Create a Hangman Game: User InterfaceAndroid hangman game preview retina
    In this series, we are creating a Hangman game for the Android platform. In the first tutorial, we set the application up to present two screens to the user and we also made a start with the user interface elements, the images and shape drawables to be precise. In the second tutorial, we will zoom in on the game's layout.Read More…
  • Code
    Android SDK
    Create a Hangman Game: Project SetupAndroid hangman game preview retina
    In this series, I will show you how to create a simple Hangman game for Android. In every tutorial, you'll learn a number of fundamental skills for creating Android applications. The application will consist of two screens, include user interaction involving adapters, contain dialogs and an action bar, and leverage XML for data storage. Let's get started.Read More…
  • Code
    Android SDK
    Android SDK: Create an Arithmetic Game - Setup and Interface CreationMath game preview@2x
    The Android platform provides the ability to facilitate interaction, to carry out computation, and to save data. To explore some of these basic features with a practical focus, this series will create a simple arithmetic game for the Android platform. The series will involve creating user interface elements and layouts, four Activity classes, and one helper class. The gameplay logic will be relatively complex if you do not have much Java experience, but you will learn to use various Java control structures along the way! Read More…