By Barry Burd

The user interface in the Android app example used here has three panels — a list of items, a detail panel describing whichever item is selected in the list, and a details-in-more-depth panel. On a small smartphone screen, each panel might be a separate activity. But a tablet screen in landscape mode has room for more than one panel.

The image below shows this app with two of the three panels. The panel on the left displays a list of Android SDK components. The panel on the right displays a description of whatever component is chosen in the list on the left. (The description is actually the first few sentences of the component’s SDK documentation.) This details-on-the-right pattern is part of many user interfaces.

Two fragments attached to one activity.

Two fragments attached to one activity.

To create the display, you build one activity. The activity has two fragments — a fragment on the left and another on the right. The left panel displays the same fragment throughout the run of the app, so you can declare that fragment in the activity’s layout file. The right panel displays one fragment at a time, but the fragment changes during the app’s run. So you declare a frame layout in the right panel.

<?xml version=“1.0” encoding=“utf-8”?>
<LinearLayout xmlns:android=
“http://schemas.android.com/apk/res/android”
android:orientation=“horizontal”
android:layout_width=“match_parent”
android:layout_height=“match_parent”>
<fragment class=
“com.allmycode.frag.ComponentNamesFragment”
android:id=“@+id/component_names”
android:layout_height=“match_parent”
android:layout_width=“0px”
android:layout_weight=“1” />
<FrameLayout android:id=“@+id/docs”
android:layout_height=“match_parent”
android:layout_width=“0px”
android:layout_weight=“1”
android:background=
“?android:attr/detailsElementBackground” />
</LinearLayout>

In the code above, the android:layout_ whatever attributes divide the screen into two halves — one half for the fragment element and the other half for the FrameLayout element. The strategy with these attributes is to start by assigning a minimum of zero pixels for each element. Of course, zero pixels means no width at all (which is the same as being invisible). To keep the two halves of the layout from being invisible, assign equal non-zero layout_weight values to the two halves. With non-zero weight values, each half expands to fill the available space in the layout.

The use of the fragment’s fully qualified class name (com.allmycode.frag.ComponentNamesFragment) is intentional. An abbreviated name (such as .ComponentNamesFragment) won’t cut the mustard.

In case you’re wondering, Android’s built-in detailsElementBackground provides a uniform look for things like the right half (things that display details about an item that the user has selected).

The app’s main activity code is impressively uninteresting.

package com.allmycode.frag;
import android.app.Activity;
import android.os.Bundle;
public class AllPurposeActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
}
}

The code below contains the ComponentNamesFragment class. By virtue of the layout in the first example, Android plants a ComponentNamesFragment on the left side of the device’s screen.

package com.allmycode.frag;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
public class ComponentNamesFragment extends ListFragment {
final static String[] COMPONENTS = { “Activity”,
“Service”, “BroadcastReceiver”, “ContentProvider” };
@Override
public void onActivityCreated
(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<>(getActivity(),
android.R.layout.simple_list_item_1, COMPONENTS));
}
@Override
public void onListItemClick(ListView l, View v,
int index, long id) {
//Create fragment with index
DocsFragment docsFragment = new DocsFragment();
Bundle args = Helper.getBundleWithIndex(index);
docsFragment.setArguments(args);
//Clear the back stack
FragmentManager fragmentManager =
getFragmentManager();
int backStackEntryCount =
fragmentManager.getBackStackEntryCount();
for (int i = 0; i < backStackEntryCount; i++) {
fragmentManager.popBackStackImmediate();
}
//Perform the transaction
FragmentTransaction fragmentTransaction =
fragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.docs, docsFragment);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}

The class in the code above extends Android’s ListFragment class. A ListFragment is a fragment that displays a list. Early on in the fragment’s lifecycle, the code in the example above sets a list adapter (more specifically, an ArrayAdapter) for the fragment. So how early is “early on”?

The ArrayAdapter constructor’s first parameter is a context. But wait! Unlike an activity, a fragment isn’t a context. So you can’t use the keyword this for the ArrayAdapter constructor’s first parameter.

Fortunately, a fragment has a getActivity method. A call to getActivity grabs the activity to which the fragment is attached. So, for the ArrayAdapter constructor’s first parameter, you can call getActivity. Of course, you can’t call getActivity until the fragment is attached to an existing activity. That’s why the fragment’s onActivityCreated method is overridden above. Android calls onActivityCreated after attaching the fragment and calling the activity’s onCreate method. So everything works as planned.

The android.app.Activity class’s great-grandparent class is android.content.Context. But the android.app.Fragment class’s parent class is plain old java.lang.Object. Therefore, in an activity’s code, the keyword this refers to a context. But in a fragment’s code, the keyword this doesn’t refer to a context.

In the example above, the constructor for the ArrayAdapter has three parameters.

The first parameter is the context — that nasty parameter that forces you to put the constructor inside the onActivityCreated method.

The second parameter is simple_list_item_1 — a standard Android layout.

The simple_list_item_ 1 layout creates the look that you see on the left side of the image. Android has lots of these standard layouts. For a roundup of the available layouts, visit Android’s Developer site.

The third parameter is the collection of items that will appear in the list.

In this example, those items come from the COMPONENTS array, which is declared in that same example.

Like a ListActivity, a ListFragment has an onListItemClick method. You can respond to a click by working with a DocsFragment, a FragmentTransaction, and a FragmentManager:

The DocsFragment represents the right side of the image above.

A fragment transaction is a bunch of things you do with fragments. For example, setting up to replace one fragment with another is a transaction.

A fragment manager does what its name suggests. It manages fragments’ arrivals and departures.