The similarity between activities and fragments makes coding fragments a familiar exercise. To show how to use the fragment API, we’ll use an example that creates a two-pane application that uses two fragments: a summary list view with hyperlinks and a web view for details, as shown in figure 20.2.
The summary/detail view is a common user interface pattern in mobility. On handsets, this pattern is typically implemented using two separate screens; but on tab- lets, with their larger screen sizes, you can have this UI pattern render both at the same time, with the summary and details information side by side. In this example, you’ll use a SummaryListFragment to contain a list view of hyperlinks and a DetailsWebFragment to display the web content that corresponds to the hyperlink that was selected in the summary fragment.
You follow three main steps when implementing a fragment:
1 Create the fragment subclass.
2 Define the fragment layout.
3 Include the fragment within the Activity.
Other steps may include the management of the back stack. Let’s begin with step 1.
20.2.1 Create the fragment subclass
Let’s first look at how to create a fragment. You can subclass Fragment or one of its subclasses:
android.app.Fragment—Represents specific behavior within an Activity such as driving part of the user interface or an operation
android.app.DialogFragment—Displays a dialog on top of its activity window
android.app.ListFragment—Displays a list of items from a data source
android.app.PreferenceFragment—Displays a hierarchy of Preference objects
android.app.WebViewFragment—Displays a WebView
PANE 1 LIST VIEW OF
WEB SITES
PANE 2 WEB VIEW WITH LOADED WEB PAGE
Figure 20.2 Dual-pane example
The following listing shows how to create a fragment using the summary pane frag- ment SummaryListFragment.
public class SummaryListFragment extends ListFragment { private String[] mListItems = new String[] {
"Mobility Weblog", "TechCrunch", "Manning Books"};
public SummaryListFragment() {}
@Override
public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState);
setListAdapter(new ArrayAdapter<String>(getActivity(), R.layout.frag_summary_textview, mListItems));
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
DetailsWebFragment frag = (DetailsWebFragment)
getFragmentManager().findFragmentById(R.id.frag_details_webview);
frag.updateDetails(mListItemsUrls[position]);
mPosition = position; // remember current position }
}
We first define SummaryListFragment, a subclass of ListFragment, which is the frag- ment that drives the left pane of the dual-pane example application. Next, fragments must have an empty constructor that is called when the fragment is instantiated dur- ing the Activity lifecycle. The system notifies the fragment that the Activity has been created B, letting the fragment initialize. In this case, the list view layout and its data source adapter are initialized C by using the ArrayAdapter that points to the simple mListItems array list; your application may instead use a cursor adapter for data that is stored in a database. Last is the list item click event handler DonList- ItemClick, which is called when a click occurs on the fragment list view. By getting the position for the selected item, we can properly update the web view by calling the web view fragment’s public method updateDetails(), which is covered shortly.
As we just mentioned, fragments must have an empty constructor that is called when the fragment is instantiated during the Activity lifecycle. At times, you’ll need to pass arguments to your fragment. One way to do so is to implement a method that instantiates the fragment and uses a Bundle and setArguments(Bundle) to pass argu- ments that are retrieved later by the fragment with getArguments(); this is shown next.
Listing 20.1 Creating the ListView fragment
Define “activity created” callback B
Initialize list view
C
Define list item click event handler
D
public static MyFragment newInstance(int arg) { MyFragment frag = new MyFragment();
Bundle args = new Bundle();
args.putInt("arg", arg);
frag.setArguments(args);
// return the fragment
return frag;
}
Using the default empty constructor create an instance of the fragment. We create a Bundle to hold the necessary arguments by calling setArguments(Bundle) to pass any arguments. Then, we return the newly created fragment with the associated arguments.
The fragment arguments can be retrieved at a later time by calling getArguments(). Next is the definition of the web view fragment DetailsWebFragment. This simple fragment displays the web content for the selected summary list view URL.
public class DetailsWebFragment extends WebViewFragment { public DetailsWebFragment() {}
WebView mWebView = getWebView();
@Override
public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState);
mWebView = getWebView();
WebSettings webSettings = mWebView.getSettings();
webSettings.setUseWebViewBackgroundForOverscrollBackground(true);
webSettings.setJavaScriptEnabled(true);
webSettings.setSavePassword(false);
webSettings.setSaveFormData(false);
webSettings.setSupportZoom(true);
webSettings.setLoadWithOverviewMode(true);
webSettings.setUseWideViewPort(true);
}
public void updateDetails(String url) { mWebView.loadUrl(url);
} }
We first define DetailsWebFragment, a subclass of WebViewFragment that drives the right side of the dual-pane example application. Again, we have an empty constructor that is called when the fragment is instantiated during the Activity lifecycle. The sys- tem calls onActivityCreated() to notify the fragment that the Activity has been created B, letting the fragment initialize. To initialize the web view C, we retrieve the
Listing 20.2 Using newInstance() to instantiate fragment with arguments
Listing 20.3 Creating the WebViewFragment
Define “activity created” callback B
Initialize web view
C
Define updateDetails to load URL
D
WebView and then initialize it (enable JavaScript, ignore password and form data, sup- port zoom). To have the web view properly fit and be resized within the allocated area of the screen, we call the methods setLoadWithOverviewMode() and setUseWide- ViewPort(). Next, the public method updateDetails() is called by the summary frag- ment to update the contents of the web view fragment when an item from the SummaryListFragment has been selected D; it takes for input the summary view selected URL that is then loaded in the web view.
We have defined a ListViewFragment that will contain the list of web sites to view and a WebViewFragment that is updated as the user selects different web sites from the list. Figure 20.3 shows the two-pane application with the list view of web sites to select from on the left side and the web content for the selected web site on the right.
Now that you’ve defined the fragments, let’s define the user interface layouts.
20.2.2 Defining a fragment layout
There are two steps in defining the fragment layout:
1 Define the fragment layout—in this case, a list view and a web view fragment.
2 Define the Activity layout.
As with activities, fragments can define a user interface either statically by using layout resources or dynamically (programmatically) at runtime. In this example, Summary- ListFragment uses a simple text view list, as shown in this listing.
Figure 20.3 Two-pane example fragment application screenshot
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:textSize="20sp"
android:gravity="fill"
android:textAppearance="?android:attr/textAppearanceMedium"
>
</TextView>
This layout is referenced during the initialization of the summary list view and the call to setListAdapter:
setListAdapter(new ArrayAdapter<String>(getActivity(), R.layout.frag_summary_textview, mListItems));
The layout can be as complex as you need; for example, it may include images, two lines, and so forth.
For the web view fragment, you aren’t defining a layout, because in this example you’re going to let the web view use that entire part of the pane to render the web content as is.
Having defined the fragment layouts, now you need to define the fragments within the Activity layout.
20.2.3 Include the fragment within the activity
Now that you’ve defined your fragments and layouts, the fragments need to be added to the Activity. You do this via the Activity layout file or programmatically. The fol- lowing listing shows how the <fragment> elements are added to the Activity layout.
<?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 android:name="com...SummaryListFragment"
android:id="@+id/frag_summary_listview"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
<fragment android:name="com...DetailsWebFragment"
android:id="@+id/frag_details_webview"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent" />
</LinearLayout>
The <fragment> element defines the fragment and associated Fragment class and attributes: in this example, SummaryListFragmentB and DetailsWebFragmentC. At
Listing 20.4 TextView for the ListFragment (frag_summary_textview.xml)
Listing 20.5 Adding fragments to an Activity layout (main.xml)
Define
SummaryListFragment
B
Define
DetailsWebFragment
C
runtime, the system replaces in-place the <fragment> element with the fragment itself within the Activity layout.
The fragments can also be added programmatically to the Activity. The following listing shows how to add the SummaryListFragment to the main layout using the frag- ment manager and transactions.
FragmentManager fragmentManager = getFragmentManager();
SummaryListFragment fragment = new SummaryListFragment();
fragmentTransaction.add(R.id.main_view, fragment);
fragmentTransaction.commit();
Note that when you’re adding multiple fragments to the same container, the order in which the fragments are added determines the order in which they will appear in the view.
You inflate the Activity’s layout as usual, when the Activity is created, by calling setContentView().
public class MainActivity extends Activity { @Override
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
setContentView(R.layout.main);
} : : }
Up to this point, we’ve defined all the major pieces for foreground fragments: how to define the fragment subclass and the fragment layout, how to include fragments within the Activity, and how a fragment can call another fragment to update its con- tent on the screen. As covered next, you can use fragments for background/non–user interface tasks as well.