1. Fragments
A Fragment represents a behavior or a portion of user interface in an Activity.
You can combine multiple fragments in a single activity to build a multi-pane UI and reuse a fragment in multiple activities. You can think of a
fragment as a modular section of an activity, which has its own lifecycle, receives its own input events, and which you can add or remove
while the activity is running (sort of like a "sub activity" that you can reuse in different activities).
A fragment must always be embedded in an activity and the fragment‘s lifecycle is directly affected by the host activity‘s lifecycle
2. Design Philosophy
For example, a news application can use one fragment to show a list of articles on the left and another fragment to display an article on the
right—both fragments appear in one activity, side by side, and each fragment has its own set of lifecycle callback methods and handle
their own user input events. Thus, instead of using one activity to select an article and another activity to read the article, the user can
select an article and read it all within the same activity, as illustrated in the tablet layout in figure 1.
3. Creating a Fragment
There are also a few subclasses that you might want to extend, instead of the base Fragment
class:
DialogFragment
- Displays a floating dialog. Using this class to create a dialog is a good alternative to using the dialog helper methods in the
Activity
class, because you can incorporate a fragment dialog into the back stack of fragments managed by the activity, allowing the user to return to a dismissed fragment. ListFragment
- Displays a list of items that are managed by an adapter (such as a
SimpleCursorAdapter
), similar toListActivity
. It provides several methods for managing a list view, such as theonListItemClick()
callback to handle click events. PreferenceFragment
- Displays a hierarchy of
Preference
objects as a list, similar toPreferenceActivity
. This is useful when creating a "settings" activity for your application.
4. Adding a user interface
To provide a layout for a fragment, you must implement the onCreateView()
callback method, which the Android system calls when it‘s time
for the fragment to draw its layout. Your implementation of this method must return a View
that is the root of your fragment‘s layout.
To return a layout from onCreateView()
, you can inflate it from a layout resource defined in XML. To help you do so, onCreateView()
provides
a LayoutInflater
object
public static class ExampleFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { // Inflate the layout for this fragment return inflater.inflate(R.layout.example_fragment, container, false); } }
The container
parameter passed to onCreateView()
is the parent ViewGroup
(from the activity‘s layout) in which your fragment layout will be
inserted.
The savedInstanceState
parameter is a Bundle
that provides data about the previous instance of the fragment, if the fragment is being resumed
The inflate()
method takes three arguments:
<1>The resource ID of the layout you want to inflate
<2>The ViewGroup
to be the parent of the inflated layout. Passing the container
is important in order for the system to apply layout
parameters to the root view of the inflated layout, specified by the parent view in which it‘s going.
<3>A boolean indicating whether the inflated layout should be attached to the ViewGroup
(the second parameter) during inflation.
(In this case, this is false because the system is already inserting the inflated layout into the container
—passing true would create a
redundant view group in the final layout.)
5. Adding a fragment to an activity
Usually, a fragment contributes a portion of UI to the host activity, which is embedded as a part of the activity‘s overall view hierarchy.
There are two ways you can add a fragment to the activity layout:
<1>Declare the fragment inside the activity‘s layout file
<?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.example.news.ArticleListFragment" android:id="@+id/list" android:layout_weight="1" android:layout_width="0dp" android:layout_height="match_parent" /> <fragment android:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
The android:name attribute in <fragment> specifies the Fragment class to instantiate in the layout
When the system creates this activity layout, it instantiates each fragment specified in the layout and calls the onCreateView()
method for each
one, to retrieve each fragment‘s layout. The system inserts the View
returned by the fragment directly in place of the <fragment>
element.
<2>Or progammatically add the fragment to an existing ViewGroup
At any time while your activity is running, you can add fragments to your activity layout. You simply need to specify a ViewGroup
in which to
place the fragment. To make fragment transactions in your activity (such as add, remove, or replace a fragment), you must use APIs
from FragmentTransaction
. You can get an instance of FragmentTransaction
from your Activity
like this:
FragmentManager fragmentManager = getFragmentManager() FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
You can then add a fragment using the add()
method, specifying the fragment to add and the view in which to insert it. For example:
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
6. Managing Fragments
To manage the fragments in your activity, you need to use FragmentManager
. To get it, call getFragmentManager()
from your activity.
7. Performing Fragment Trasactions
A great feature about using fragments in your activity is the ability to add, remove, replace, and perform other actions with them, in response
to user interaction. Each set of changes that you commit to the activity is called a transaction and you can perform one using APIs in
FragmentTransaction
. You can also save each transaction to a back stack managed by the activity, allowing the user to navigate backward
through the fragment changes (similar to navigating backward through activities).
Each transaction is a set of changes that you want to perform at the same time. You can set up all the changes you want to perform for a
given transaction using methods such as add()
, remove()
, and replace()
. Then, apply the transaction to the activity, you must call commit()
.
For example, here‘s how you can replace one fragment with another, and preserve the previous state in the back stack:
// Create new fragment and transaction Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // Commit the transaction transaction.commit();
8. Communicating with the Activity
Although a Fragment
is implemented as an object that‘s independent from an Activity
and can be used inside multiple activities, a given
instance of a fragment is directly tied to the activity that contains it.
View listView = getActivity().findViewById(R.id.list);
9. Creating event callback to the activity
In some cases, you might need a fragment to share events with the activity. A good way to do that is to define a callback interface inside the
fragment and require that the host activity implement it. When the activity receives a callback through the interface, it can share the
information with other fragments in the layout as necessary.
For example, if a news application has two fragments in an activity—one to show a list of articles (fragment A) and another to display an
article (fragment B)—then fragment A must tell the activity when a list item is selected so that it can tell fragment B to display the article.
In this case, the OnArticleSelectedListener
interface is declared inside fragment A:
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... }
Then the activity that hosts the fragment implements the OnArticleSelectedListener
interface and overrides onArticleSelected()
to notify
fragment B of the event from fragment A.
To ensure that the host activity implements this interface, fragment A‘s onAttach()
callback method (which the system calls when adding the
fragment to the activity) instantiates an instance of OnArticleSelectedListener
by casting the Activity
that is passed into onAttach()
:
public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; ... @Override public void onAttach(Activity activity) { super.onAttach(activity); try { mListener = (OnArticleSelectedListener) activity; } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement OnArticleSelectedListener"); } } ... }
If the activity has not implemented the interface, then the fragment throws a ClassCastException
For example, if fragment A is an extension of ListFragment
, each time the user clicks a list item, the system calls onListItemClick()
in the
fragment, which then calls onArticleSelected()
to share the event with the activity
public static class FragmentA extends ListFragment { OnArticleSelectedListener mListener; ... @Override public void onListItemClick(ListView l, View v, int position, long id) { // Append the clicked item‘s row ID with the content provider Uri Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id); // Send the event and Uri to the host activity mListener.onArticleSelected(noteUri); } ... }
10. Fragment Lifecycle
11. Examples
To bring everything discussed in this document together, here‘s an example of an activity using two fragments to create a two-pane layout.
The activity below includes one fragment to show a list of Shakespeare play titles and another to show a summary of the play when
selected from the list. It also demonstrates how to provide different configurations of the fragments, based on the screen configuration.