Fragments

来源:互联网 发布:萧井陌 编程入门指南 编辑:程序博客网 时间:2024/05/22 16:03

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. For example, when the activity is paused, so are all fragments in it, and when the activity is destroyed, so are all fragments. However, while an activity is running (it is in the resumed lifecycle state), you can manipulate each fragment independently, such as add or remove them. When you perform such a fragment transaction, you can also add it to a back stack that's managed by the activity—each back stack entry in the activity is a record of the fragment transaction that occurred. The back stack allows the user to reverse a fragment transaction (navigate backwards), by pressing the Back button.

When you add a fragment as a part of your activity layout, it lives in aViewGroup inside the activity's view hierarchy and the fragment defines its own view layout. You can insert a fragment into your activity layout by declaring the fragment in the activity's layout file, as a <fragment> element, or from your application code by adding it to an existing ViewGroup. However, a fragment is not required to be a part of the activity layout; you may also use a fragment without its own UI as an invisible worker for the activity.

This document describes how to build your application to use fragments, including how fragments can maintain their state when added to the activity's back stack, share events with the activity and other fragments in the activity, contribute to the activity's action bar, and more.

Design Philosophy


Android introduced fragments in Android 3.0 (API level 11), primarily to support more dynamic and flexible UI designs on large screens, such as tablets. Because a tablet's screen is much larger than that of a handset, there's more room to combine and interchange UI components. Fragments allow such designs without the need for you to manage complex changes to the view hierarchy. By dividing the layout of an activity into fragments, you become able to modify the activity's appearance at runtime and preserve those changes in a back stack that's managed by the activity.

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.

You should design each fragment as a modular and reusable activity component. That is, because each fragment defines its own layout and its own behavior with its own lifecycle callbacks, you can include one fragment in multiple activities, so you should design for reuse and avoid directly manipulating one fragment from another fragment. This is especially important because a modular fragment allows you to change your fragment combinations for different screen sizes. When designing your application to support both tablets and handsets, you can reuse your fragments in different layout configurations to optimize the user experience based on the available screen space. For example, on a handset, it might be necessary to separate fragments to provide a single-pane UI when more than one cannot fit within the same activity.

Figure 1. An example of how two UI modules defined by fragments can be combined into one activity for a tablet design, but separated for a handset design.

For example—to continue with the news application example—the application can embed two fragments inActivity A, when running on a tablet-sized device. However, on a handset-sized screen, there's not enough room for both fragments, so Activity A includes only the fragment for the list of articles, and when the user selects an article, it starts Activity B, which includes the second fragment to read the article. Thus, the application supports both tablets and handsets by reusing fragments in different combinations, as illustrated in figure 1.

For more information about designing your application with different fragment combinations for different screen configurations, see the guide to Supporting Tablets and Handsets.

Creating a Fragment


Figure 2. The lifecycle of a fragment (while its activity is running).

To create a fragment, you must create a subclass ofFragment (or an existing subclass of it). The Fragmentclass has code that looks a lot like an Activity. It contains callback methods similar to an activity, such asonCreate()onStart()onPause(), and onStop(). In fact, if you're converting an existing Android application to use fragments, you might simply move code from your activity's callback methods into the respective callback methods of your fragment.

Usually, you should implement at least the following lifecycle methods:

onCreate()
The system calls this when creating the fragment. Within your implementation, you should initialize essential components of the fragment that you want to retain when the fragment is paused or stopped, then resumed.
onCreateView()
The system calls this when it's time for the fragment to draw its user interface for the first time. To draw a UI for your fragment, you must return a View from this method that is the root of your fragment's layout. You can return null if the fragment does not provide a UI.
onPause()
The system calls this method as the first indication that the user is leaving the fragment (though it does not always mean the fragment is being destroyed). This is usually where you should commit any changes that should be persisted beyond the current user session (because the user might not come back).

Most applications should implement at least these three methods for every fragment, but there are several other callback methods you should also use to handle various stages of the fragment lifecycle. All the lifecycle callback methods are discussed in more detail in the section about Handling the Fragment Lifecycle.

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 the onListItemClick()callback to handle click events.
PreferenceFragment
Displays a hierarchy of Preference objects as a list, similar to PreferenceActivity. This is useful when creating a "settings" activity for your application.

Adding a user interface

A fragment is usually used as part of an activity's user interface and contributes its own layout to the activity.

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.

Note: If your fragment is a subclass of ListFragment, the default implementation returns a ListView fromonCreateView(), so you don't need to implement it.

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.

For example, here's a subclass of Fragment that loads a layout from the example_fragment.xml file:

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 savedInstanceStateparameter is a Bundle that provides data about the previous instance of the fragment, if the fragment is being resumed (restoring state is discussed more in the section about Handling the Fragment Lifecycle).

The inflate() method takes three arguments:

  • The resource ID of the layout you want to inflate.
  • 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.
  • 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.)

Now you've seen how to create a fragment that provides a layout. Next, you need to add the fragment to your activity.

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:

  • Declare the fragment inside the activity's layout file.

    In this case, you can specify layout properties for the fragment as if it were a view. For example, here's the layout file for an activity with two fragments:

    <?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 the <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 theonCreateView() method for each one, to retrieve each fragment's layout. The system inserts the Viewreturned by the fragment directly in place of the <fragment> element.

    Note: Each fragment requires a unique identifier that the system can use to restore the fragment if the activity is restarted (and which you can use to capture the fragment to perform transactions, such as remove it). There are three ways to provide an ID for a fragment:

    • Supply the android:id attribute with a unique ID.
    • Supply the android:tag attribute with a unique string.
    • If you provide neither of the previous two, the system uses the ID of the container view.
  • Or, programmatically 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 Activitylike 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();

    The first argument passed to add() is the ViewGroup in which the fragment should be placed, specified by resource ID, and the second parameter is the fragment to add.

    Once you've made your changes with FragmentTransaction, you must call commit() for the changes to take effect.

Adding a fragment without a UI

The examples above show how to add a fragment to your activity in order to provide a UI. However, you can also use a fragment to provide a background behavior for the activity without presenting additional UI.

To add a fragment without a UI, add the fragment from the activity using add(Fragment, String) (supplying a unique string "tag" for the fragment, rather than a view ID). This adds the fragment, but, because it's not associated with a view in the activity layout, it does not receive a call to onCreateView(). So you don't need to implement that method.

Supplying a string tag for the fragment isn't strictly for non-UI fragments—you can also supply string tags to fragments that do have a UI—but if the fragment does not have a UI, then the string tag is the only way to identify it. If you want to get the fragment from the activity later, you need to use findFragmentByTag().

For an example activity that uses a fragment as a background worker, without a UI, see theFragmentRetainInstance.java sample, which is included in the SDK samples (available through the Android SDK Manager) and located on your system as<sdk_root>/APIDemos/app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java.

Managing Fragments


To manage the fragments in your activity, you need to use FragmentManager. To get it, callgetFragmentManager() from your activity.

Some things that you can do with FragmentManager include:

  • Get fragments that exist in the activity, with findFragmentById() (for fragments that provide a UI in the activity layout) or findFragmentByTag() (for fragments that do or don't provide a UI).
  • Pop fragments off the back stack, with popBackStack() (simulating a Back command by the user).
  • Register a listener for changes to the back stack, with addOnBackStackChangedListener().

For more information about these methods and others, refer to the FragmentManager class documentation.

As demonstrated in the previous section, you can also use FragmentManager to open a FragmentTransaction, which allows you to perform transactions, such as add and remove fragments.

Performing Fragment Transactions


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).

You can acquire an instance of FragmentTransaction from the FragmentManager like this:

FragmentManager fragmentManager = getFragmentManager();FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

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, to apply the transaction to the activity, you must call commit().

Before you call commit(), however, you might want to call addToBackStack(), in order to add the transaction to a back stack of fragment transactions. This back stack is managed by the activity and allows the user to return to the previous fragment state, by pressing the Back button.

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 transactionFragment 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 stacktransaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);// Commit the transactiontransaction.commit();

In this example, newFragment replaces whatever fragment (if any) is currently in the layout container identified by the R.id.fragment_container ID. By calling addToBackStack(), the replace transaction is saved to the back stack so the user can reverse the transaction and bring back the previous fragment by pressing the Backbutton.

If you add multiple changes to the transaction (such as another add() or remove()) and calladdToBackStack(), then all changes applied before you call commit() are added to the back stack as a single transaction and the Back button will reverse them all together.

The order in which you add changes to a FragmentTransaction doesn't matter, except:

  • You must call commit() last
  • If you're adding multiple fragments to the same container, then the order in which you add them determines the order they appear in the view hierarchy

If you do not call addToBackStack() when you perform a transaction that removes a fragment, then that fragment is destroyed when the transaction is committed and the user cannot navigate back to it. Whereas, if you do call addToBackStack() when removing a fragment, then the fragment is stopped and will be resumed if the user navigates back.

Tip: For each fragment transaction, you can apply a transition animation, by calling setTransition() before you commit.

Calling commit() does not perform the transaction immediately. Rather, it schedules it to run on the activity's UI thread (the "main" thread) as soon as the thread is able to do so. If necessary, however, you may callexecutePendingTransactions() from your UI thread to immediately execute transactions submitted bycommit(). Doing so is usually not necessary unless the transaction is a dependency for jobs in other threads.

Caution: You can commit a transaction using commit() only prior to the activity saving its state (when the user leaves the activity). If you attempt to commit after that point, an exception will be thrown. This is because the state after the commit can be lost if the activity needs to be restored. For situations in which its okay that you lose the commit, use commitAllowingStateLoss().

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.

Specifically, the fragment can access the Activity instance with getActivity() and easily perform tasks such as find a view in the activity layout:

View listView = getActivity().findViewById(R.id.list);

Likewise, your activity can call methods in the fragment by acquiring a reference to the Fragment fromFragmentManager, using findFragmentById() or findFragmentByTag(). For example:

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

Creating event callbacks 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 overridesonArticleSelected() 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. On success, the mListener member holds a reference to activity's implementation of OnArticleSelectedListener, so that fragment A can share events with the activity by calling methods defined by the OnArticleSelectedListenerinterface. 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);    }    ...}

The id parameter passed to onListItemClick() is the row ID of the clicked item, which the activity (or other fragment) uses to fetch the article from the application's ContentProvider.

More information about using a content provider is available in the Content Providers document.

Adding items to the App Bar

Your fragments can contribute menu items to the activity's Options Menu (and, consequently, the app bar) by implementing onCreateOptionsMenu(). In order for this method to receive calls, however, you must callsetHasOptionsMenu() during onCreate(), to indicate that the fragment would like to add items to the Options Menu (otherwise, the fragment will not receive a call to onCreateOptionsMenu()).

Any items that you then add to the Options Menu from the fragment are appended to the existing menu items. The fragment also receives callbacks to onOptionsItemSelected() when a menu item is selected.

You can also register a view in your fragment layout to provide a context menu by callingregisterForContextMenu(). When the user opens the context menu, the fragment receives a call toonCreateContextMenu(). When the user selects an item, the fragment receives a call toonContextItemSelected().

Note: Although your fragment receives an on-item-selected callback for each menu item it adds, the activity is first to receive the respective callback when the user selects a menu item. If the activity's implementation of the on-item-selected callback does not handle the selected item, then the event is passed to the fragment's callback. This is true for the Options Menu and context menus.

For more information about menus, see the Menus developer guide and the App Bar training class.

Handling the Fragment Lifecycle


Figure 3. The effect of the activity lifecycle on the fragment lifecycle.

Managing the lifecycle of a fragment is a lot like managing the lifecycle of an activity. Like an activity, a fragment can exist in three states:

Resumed
The fragment is visible in the running activity.
Paused
Another activity is in the foreground and has focus, but the activity in which this fragment lives is still visible (the foreground activity is partially transparent or doesn't cover the entire screen).
Stopped
The fragment is not visible. Either the host activity has been stopped or the fragment has been removed from the activity but added to the back stack. A stopped fragment is still alive (all state and member information is retained by the system). However, it is no longer visible to the user and will be killed if the activity is killed.

Also like an activity, you can retain the state of a fragment using a Bundle, in case the activity's process is killed and you need to restore the fragment state when the activity is recreated. You can save the state during the fragment'sonSaveInstanceState() callback and restore it during either onCreate()onCreateView(), oronActivityCreated(). For more information about saving state, see the Activities document.

The most significant difference in lifecycle between an activity and a fragment is how one is stored in its respective back stack. An activity is placed into a back stack of activities that's managed by the system when it's stopped, by default (so that the user can navigate back to it with the Back button, as discussed in Tasks and Back Stack). However, a fragment is placed into a back stack managed by the host activity only when you explicitly request that the instance be saved by calling addToBackStack() during a transaction that removes the fragment.

Otherwise, managing the fragment lifecycle is very similar to managing the activity lifecycle. So, the same practices for managing the activity lifecycle also apply to fragments. What you also need to understand, though, is how the life of the activity affects the life of the fragment.

Caution: If you need a Context object within your Fragment, you can call getActivity(). However, be careful to call getActivity() only when the fragment is attached to an activity. When the fragment is not yet attached, or was detached during the end of its lifecycle, getActivity() will return null.

Coordinating with the activity lifecycle

The lifecycle of the activity in which the fragment lives directly affects the lifecycle of the fragment, such that each lifecycle callback for the activity results in a similar callback for each fragment. For example, when the activity receives onPause(), each fragment in the activity receives onPause().

Fragments have a few extra lifecycle callbacks, however, that handle unique interaction with the activity in order to perform actions such as build and destroy the fragment's UI. These additional callback methods are:

onAttach()
Called when the fragment has been associated with the activity (the Activity is passed in here).
onCreateView()
Called to create the view hierarchy associated with the fragment.
onActivityCreated()
Called when the activity's onCreate() method has returned.
onDestroyView()
Called when the view hierarchy associated with the fragment is being removed.
onDetach()
Called when the fragment is being disassociated from the activity.

The flow of a fragment's lifecycle, as it is affected by its host activity, is illustrated by figure 3. In this figure, you can see how each successive state of the activity determines which callback methods a fragment may receive. For example, when the activity has received its onCreate() callback, a fragment in the activity receives no more than the onActivityCreated() callback.

Once the activity reaches the resumed state, you can freely add and remove fragments to the activity. Thus, only while the activity is in the resumed state can the lifecycle of a fragment change independently.

However, when the activity leaves the resumed state, the fragment again is pushed through its lifecycle by the activity.

Example


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.

Note: The complete source code for this activity is available in FragmentLayout.java.

The main activity applies a layout in the usual way, during onCreate():

@Overrideprotected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.fragment_layout);}

The layout applied is fragment_layout.xml:

<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.example.android.apis.app.FragmentLayout$TitlesFragment"            android:id="@+id/titles" android:layout_weight="1"            android:layout_width="0px" android:layout_height="match_parent" />    <FrameLayout android:id="@+id/details" android:layout_weight="1"            android:layout_width="0px" android:layout_height="match_parent"            android:background="?android:attr/detailsElementBackground" /></LinearLayout>

Using this layout, the system instantiates the TitlesFragment (which lists the play titles) as soon as the activity loads the layout, while the FrameLayout (where the fragment for showing the play summary will go) consumes space on the right side of the screen, but remains empty at first. As you'll see below, it's not until the user selects an item from the list that a fragment is placed into the FrameLayout.

However, not all screen configurations are wide enough to show both the list of plays and the summary, side by side. So, the layout above is used only for the landscape screen configuration, by saving it at res/layout-land/fragment_layout.xml.

Thus, when the screen is in portrait orientation, the system applies the following layout, which is saved atres/layout/fragment_layout.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:layout_width="match_parent" android:layout_height="match_parent">    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"            android:id="@+id/titles"            android:layout_width="match_parent" android:layout_height="match_parent" /></FrameLayout>

This layout includes only TitlesFragment. This means that, when the device is in portrait orientation, only the list of play titles is visible. So, when the user clicks a list item in this configuration, the application will start a new activity to show the summary, instead of loading a second fragment.

Next, you can see how this is accomplished in the fragment classes. First is TitlesFragment, which shows the list of Shakespeare play titles. This fragment extends ListFragment and relies on it to handle most of the list view work.

As you inspect this code, notice that there are two possible behaviors when the user clicks a list item: depending on which of the two layouts is active, it can either create and display a new fragment to show the details in the same activity (adding the fragment to the FrameLayout), or start a new activity (where the fragment can be shown).

public static class TitlesFragment extends ListFragment {    boolean mDualPane;    int mCurCheckPosition = 0;    @Override    public void onActivityCreated(Bundle savedInstanceState) {        super.onActivityCreated(savedInstanceState);        // Populate list with our static array of titles.        setListAdapter(new ArrayAdapter<String>(getActivity(),                android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));        // Check to see if we have a frame in which to embed the details        // fragment directly in the containing UI.        View detailsFrame = getActivity().findViewById(R.id.details);        mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;        if (savedInstanceState != null) {            // Restore last state for checked position.            mCurCheckPosition = savedInstanceState.getInt("curChoice", 0);        }        if (mDualPane) {            // In dual-pane mode, the list view highlights the selected item.            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);            // Make sure our UI is in the correct state.            showDetails(mCurCheckPosition);        }    }    @Override    public void onSaveInstanceState(Bundle outState) {        super.onSaveInstanceState(outState);        outState.putInt("curChoice", mCurCheckPosition);    }    @Override    public void onListItemClick(ListView l, View v, int position, long id) {        showDetails(position);    }    /**     * Helper function to show the details of a selected item, either by     * displaying a fragment in-place in the current UI, or starting a     * whole new activity in which it is displayed.     */    void showDetails(int index) {        mCurCheckPosition = index;        if (mDualPane) {            // We can display everything in-place with fragments, so update            // the list to highlight the selected item and show the data.            getListView().setItemChecked(index, true);            // Check what fragment is currently shown, replace if needed.            DetailsFragment details = (DetailsFragment)                    getFragmentManager().findFragmentById(R.id.details);            if (details == null || details.getShownIndex() != index) {                // Make new fragment to show this selection.                details = DetailsFragment.newInstance(index);                // Execute a transaction, replacing any existing fragment                // with this one inside the frame.                FragmentTransaction ft = getFragmentManager().beginTransaction();                if (index == 0) {                    ft.replace(R.id.details, details);                } else {                    ft.replace(R.id.a_item, details);                }                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);                ft.commit();            }        } else {            // Otherwise we need to launch a new activity to display            // the dialog fragment with selected text.            Intent intent = new Intent();            intent.setClass(getActivity(), DetailsActivity.class);            intent.putExtra("index", index);            startActivity(intent);        }    }}

The second fragment, DetailsFragment shows the play summary for the item selected from the list fromTitlesFragment:

public static class DetailsFragment extends Fragment {    /**     * Create a new instance of DetailsFragment, initialized to     * show the text at 'index'.     */    public static DetailsFragment newInstance(int index) {        DetailsFragment f = new DetailsFragment();        // Supply index input as an argument.        Bundle args = new Bundle();        args.putInt("index", index);        f.setArguments(args);        return f;    }    public int getShownIndex() {        return getArguments().getInt("index", 0);    }    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container,            Bundle savedInstanceState) {        if (container == null) {            // We have different layouts, and in one of them this            // fragment's containing frame doesn't exist.  The fragment            // may still be created from its saved state, but there is            // no reason to try to create its view hierarchy because it            // won't be displayed.  Note this is not needed -- we could            // just run the code below, where we would create and return            // the view hierarchy; it would just never be used.            return null;        }        ScrollView scroller = new ScrollView(getActivity());        TextView text = new TextView(getActivity());        int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,                4, getActivity().getResources().getDisplayMetrics());        text.setPadding(padding, padding, padding, padding);        scroller.addView(text);        text.setText(Shakespeare.DIALOGUE[getShownIndex()]);        return scroller;    }}

Recall from the TitlesFragment class, that, if the user clicks a list item and the current layout does not include the R.id.details view (which is where the DetailsFragment belongs), then the application starts theDetailsActivity activity to display the content of the item.

Here is the DetailsActivity, which simply embeds the DetailsFragment to display the selected play summary when the screen is in portrait orientation:

public static class DetailsActivity extends Activity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        if (getResources().getConfiguration().orientation                == Configuration.ORIENTATION_LANDSCAPE) {            // If the screen is now in landscape mode, we can show the            // dialog in-line with the list so we don't need this activity.            finish();            return;        }        if (savedInstanceState == null) {            // During initial setup, plug in the details fragment.            DetailsFragment details = new DetailsFragment();            details.setArguments(getIntent().getExtras());            getFragmentManager().beginTransaction().add(android.R.id.content, details).commit();        }    }}

Notice that this activity finishes itself if the configuration is landscape, so that the main activity can take over and display the DetailsFragment alongside the TitlesFragment. This can happen if the user begins theDetailsActivity while in portrait orientation, but then rotates to landscape (which restarts the current activity).

For more samples using fragments (and complete source files for this example), see the API Demos sample app available in ApiDemos (available for download from the Samples SDK component).


一个碎片代表了一个行为或在用户界面的部分 活动。您可以在一个单一的活动结合多个片段来构建一个多窗格界面和重用在多个活动碎片。你可以把一个片段作为一个活动,它有自己的生命周期的一个模块化部分,接收其自己的输入事件,并可以添加或活动运行时卸下(有点像一个“子活动”,你可以在不同的活动重用)。

碎片必须始终嵌入的活动和片段的生命周期中是直接受主机活动的生命周期。例如,当活动暂停,所以是在它的所有片段,并且当活动被破坏,因此是所有碎片。然而,活动运行(这是在同时恢复 生命周期状态),可以独立操作每个片段,如添加或删除它们。当您执行这样一个碎片的交易,你也可以将其添加至由该活动的每一个后退堆栈中的活动条目管理是发生的片段交易的记录一回堆栈。背面堆栈允许用户翻转碎片交易(导航向后),按后退按钮。

当您添加一个碎片作为你的活动布局的一部分,它生活在一个ViewGroup中活动的视图层次内部和片段定义了自己的看法布局。您可以通过声明在活动的布局文件碎片作为插入碎片到您的活动布局<碎片>将其添加到现有的元素,或从应用程序代码的ViewGroup。然而,要在活动布局的一部分的碎片不是必需的; 你也可以使用一个碎片,没有它自己的UI作为活动的一种无形的工人。

本文介绍了如何构建你的应用程序中使用的碎片段,包括什么时候加入到活动的回栈碎片如何能保持自己的状态,在该活动的活动和其他碎片共享活动,有助于活动的行动吧,和更多。

设计理念


在Android的Andr​​oid 3.0的(API级别11)介绍片段,主要是为了支持在大屏幕上更具活力和灵活的用户界面设计,如平板电脑。由于平板电脑的屏幕比手机大很多,有更多的空间来结合和交换UI组件。片段允许这样的设计,而不需要你来管理视图层次结构复杂的变化。通过将一个活动成片段的布局,成为您能够修改活动的外观在运行时和保存一回堆栈是由活动管理的这些变化。

例如,一个消息应用程序可以使用一个碎片来显示的左侧和另一片段的文章的列表来显示上的一篇文章右两个片段出现在一个活动,并排,并且每个碎片具有其自己的一组的生命周期的回调方法和处理自己的用户输入事件。因而,代替使用一个活动选择的物品和另一个活动阅读文章中,用户可以选择一篇文章,并且在同一活动内读这一切,如在图1中的片碎片布局示出。

你应该设计每个碎片作为一个模块化和可重用活性组分。也就是说,因为每个碎片定义自己的布局,并有自己的生命周期回调自身的行为,您可以在多种活动一个碎片,所以你应该设计重用,并避免直接从另一个碎片操纵一个片段。这一点尤其重要,因为模块化的碎片让你改变你的碎片组合为不同的屏幕尺寸。在设计应用程序以支持平板电脑和手机,你可以重用碎片不同的布局结构优化基础上,可用的屏幕空间的用户体验。例如,在手机上,可能有必要以分离的碎片,以提供一个单窗格UI时多于一个不能在同一活动内适合。

图1如何由碎片定义的两个用户界面模块可以组合成用于片碎片设计一种活性,但对于一个手机设计分开的一个例子。

例如,继续与消息应用例-该应用程序可以嵌入在两个片段活动A,片剂大小的装置上运行时。然而,手持机尺寸屏幕上,没有足够的空间,这两个碎片活动A仅包括的物品列表中的碎片,并且当用户选择的物品,它启动 活动B,其中包括第二个片段来读取这篇文章。因此,应用程序通过以不同的组合重用碎片,如在图1中所示支持片剂和手机。

有关设计为不同的屏幕配置不同的碎片组合应用程序的更多信息,请参阅该向导支碎片和手机。

创建碎片


图2(同时其活动正在运行)的片段的生命周期。

要创建一个碎片,您必须创建的子类片段 片段(或它的一个子类存在的)。该片段 片段类有代码看起来很像一个活动。它包含类似于一个活动回调方法,如的onCreate()在onStart() , 的onPause() ,和的onStop() 。事实上,如果你把现有的Android应用程序中使用的碎片,你可能只需移动从活动的回调方法的代码到你的片段各自的回调方法。

通常情况下,你应该实现至少下列生命周期方法:

的onCreate()
创建碎片时,系统调用此。在您的实现,你应该初始化要当片段被暂停或停止保留片段的重要组成部分,然后重新开始。
onCreateView()
系统调用这个当它的时间段来绘制其用户界面的第一次。要绘制您片段的UI,你必须返回一个视图从这个方法就是你的碎片布局的根。可以返回null如果片段不提供的UI。
的onPause()
系统调用此方法如用户正在离开片段(尽管它并不总是意味着该片段被破坏)的第一指示。这通常是你应该犯应当超越当前用户会话被持久化(因为用户可能不会回来)的任何变化。

大多数应用程序应该实现至少每片段这三种方法,但也有你也应该用它来 ​​处理片段生命周期的不同阶段其他几个回调方法。所有生命周期回调方法中更详细地了解该部分中讨论的处理的片段生命周期。

也有几个子类,而不是基地,你可能要扩展,片段类:

DialogFragment
显示一个浮动对话框。使用这个类来创建一个对话框是一个很好的替代使用在该对话框的helper方法的活动类,因为你可以将一个片段对话进入由活动管理的片段回栈,允许用户返回到被解雇片段。
ListFragment
显示由一个适配器(如一个管理项目的列表SimpleCursorAdapter),类似于ListActivity。它提供了一种用于管理列表视图几种方法,如onListItemClick()回调处理单击事件。
PreferenceFragment
显示的层级偏好的对象作为一个列表,类似于 PreferenceActivity。为应用程序创建一个“设置”活动时,这是非常有用的。

加入的用户接口

片段通常用作一个活动的用户界面的一部分,并有助于其自身的布局的活性。

为了提供一个片段的布局,你必须实现onCreateView()回调方法,当它的时间片段绘制它的布局,Android系统调用。你的这个方法的实现必须返回 查看这是你的片段布局的根。

注意:如果您的片段是的子类ListFragment,默认实现返回ListView控件从 onCreateView() ,所以你不需要实现它。

返回从布局onCreateView() ,您可以从它充气布局资源的XML定义。为了帮助你这样做,onCreateView()提供了一个 LayoutInflater对象。

例如,下面的子类片段加载从布局 example_fragment.xml文件:

public  static  class  ExampleFragment  extends  Fragment  {     @Override     public  View onCreateView ( LayoutInflater inflater ,  ViewGroup container ,                              Bundle savedInstanceState )  {         //膨胀此片段的布局        返回充气膨胀ř 布局example_fragment 容器 );     } }

所述容器传递给参数onCreateView()是父的ViewGroup其中的片段布局将被插入(从该活动的布局)。该savedInstanceState参数是一个,它提供的数据有关片段的一个实例,如果该片段被恢复(恢复状态更多有关节讨论处理片段生命周期)。

充气()方法有三个参数:

  • 布局的资源ID要膨胀。
  • 的ViewGroup是布局膨胀的母公司。传递容器是为了重要的系统中应用的布局参数充气布局的根视图,由其中它的将父视图指定。
  • 一个布尔值,指示是否虚增布局应连接到的ViewGroup通胀期间(第二个参数)。(在这种情况下,这是假的,因为系统已经插入充气布局进入容器 -passing真正会产生在最终布局冗余视图基。)

现在,你已经看到了如何创建提供了一个布局片段。接下来,你需要将片段添加到您的活动。

加入的片段到活动

通常,一个片段有助于用户界面的主机的活动,这被嵌入作为活动的整体图层次结构的一部分的部分。有两种方法可以将片段添加到活动布局:

  • 声明活动的布局文件中的片段。

    在这种情况下,可以就好像它是一个视图片段指定布局属性。例如,这是一个有两个片段活动的布局文件:

    <?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>

    机器人:名称在属性<片段>指定片段类布局实例。

    当系统创建这个活动的布局,它实例化布局指定的每个片段,并调用onCreateView()为每一个方法,检索每个片段的布局。该系统插入视图的片段直接到位的返回<片段>元素。

    注:每个片段都需要一个唯一的标识符,该系统可以使用恢复片段,如果该活动将重新启动(并且你可以用它来 ​​捕捉片段进行交易,如删除)。有三种方式是提供一种用于片段的ID:

    • 供应机器人:ID属性有唯一的ID。
    • 供应机器人:标签属性带有一个唯一的字符串。
    • 如果提供既不前两个的,本系统使用的容器视图的ID。
  • 或者,该片段编程添加到现有的ViewGroup

    在当您的活动正在运行的任何时候,你可以添加片段您的活动布局。你只需要指定一个ViewGroup中要放置片段。

    为了使片段交易你的活动(如添加,删除或替换的片段),您必须使用API中FragmentTransaction。你可以得到的一个实例FragmentTransaction从你的活动是这样的:

    FragmentManager fragmentManager =  getFragmentManager () ; FragmentTransaction fragmentTransaction = fragmentManager . beginTransaction () ;

    然后,您可以使用添加一个片段的add()方法,指定要添加片段并在其中插入该视图。例如:

    ExampleFragment fragment =  new  ExampleFragment (); fragmentTransaction . add ( R . id . fragment_container , fragment ); fragmentTransaction . commit ();

    传递给第一个参数加() 是所述的ViewGroup在其中片段应放置,由资源ID指定的,第二个参数是添加片段。

    一旦您完成了自己的更改 FragmentTransaction,必须调用提交(),以使更改生效。

添加一个片段没有UI

上面的例子说明如何将片段以提供一个UI添加到您的活动。不过,你也可以使用一个片段,为活动背景的行为,而不提交补充UI。

要添加一个片段没有用户界面,使用从活动添加片段添加(片段,字符串)(提供一个唯一的字符串“标记”的片段,而不是一个视图ID)。这增加了该片段,但是,因为它不与在活动布局的视图相关联的,它不接收到一个呼叫onCreateView() 。所以你不要需要实现的方法。

供给的字符串标签的片段是不严格用于非UI片段-还可以提供字符串标签到确有一个片段的UI,但如果该片段不具有用户界面,则该串标记是唯一的方式识别它。如果以后要获得该活动的片段,你需要使用findFragmentByTag() 

对于使用片段作为背景的工人,没有一个UI的例子活动,见FragmentRetainInstance.java样品,这是(通过Android SDK管理器中可用)包含在SDK样本中,位于您的系统上的 <sdk_root> / APIDemos /app/src/main/java/com/example/android/apis/app/FragmentRetainInstance.java

管理片段


要管理你的活动片段,你需要使用FragmentManager。为了得到它,调用getFragmentManager()从您的活动。

有些事情,你可以做与FragmentManager包括:

  • 获得存在于活性的片段,与findFragmentById() (用于在活动的布局提供的UI片段)或findFragmentByTag() (用于或不提供一个UI片段)。
  • 弹出的碎片断背堆栈,popBackStack() (模拟返回用户命令)。
  • 注册更改回堆栈的侦听器,具有addOnBackStackChangedListener() 

有关这些方法和其他的更多信息,请参阅FragmentManager类文档。

正如上一节所演示的,你也可以使用FragmentManager 打开FragmentTransaction,它允许你进行交易,如添加和删除片段。

执行片段交易


关于您的活动使用的片段一个很大的特点是添加,删除,替换,并与他们进行其他操作,响应用户交互的能力。每套你提交到活动的变化被称为一个事务,您可以在执行一个使用API FragmentTransaction。也可以每个事务保存于由活动管理的回栈,允许用户通过该片段的变化(类似于通过活动向后导航)向后导航。

您可以获取的实例FragmentTransactionFragmentManager是这样的:

FragmentManager fragmentManager =  getFragmentManager () ; FragmentTransaction fragmentTransaction = fragmentManager . beginTransaction () ;

每个事务是一组要在同一时间进行的变化。您可以使用诸如设置所有你想给定事务执行更改的add() 删除()REPLACE() 。然后,向交易应用到活动,必须调用提交() 

致电前提交() ,但是,您可能要调用addToBackStack() ,以交易添加到片段交易的背面栈。此回栈是由活动管理和使用户能够返回到前一片段的状态,通过按下返回按钮。

例如,这里是你如何可以与另一个替换一个片段,并在后面的堆栈保留以前的状态:

//创建新的片段   更换无论是在这个片段fragment_container观点,//和事务添加到后退堆栈交易更换ř ID fragment_container newFragment ); 交易addToBackStack ); //提交事务的事务提交( );

在这个例子中,newFragment替换任何片段(如果有的话)是目前在由所识别的布局容器R.id.fragment_container标识。通过调用addToBackStack() ,替换事务保存到后退堆栈,使用户可以扭转交易,按带回以前的片段后退按钮。

如果添加多个变化到事务(如另一个的add()remove()方法)并调用addToBackStack() ,那么所有的更改你打电话之前提交()被添加到后退堆栈作为一个单一的交易和后退按钮将扭转它们放在一起。

在其中添加更改为顺序FragmentTransaction不要紧,以下情况除外:

  • 必须调用的commit()最后
  • 如果您要添加多个片段到同一容器中,然后在其中添加的顺序决定了它们在视图层次结构的顺序

如果你不叫addToBackStack()当你进行交易,消除片段,则该片段时提交事务破坏,用户无法导航回 ​​到它。然而,如果你调用addToBackStack()删除片段时,则片段停止,如果用户返回将会恢复。

提示:对于每一个片段的交易,你可以申请一个过渡动画,通过调用setTransition()在你提交之前。

调用的commit()并不立即执行交易。相反,它安排其到活动的UI线程(以下简称“主”线程)上只要线程能够这样做的运行。如果有必要,但是,您可致电executePendingTransactions()从你的UI线程立即执行提交的事务提交() 。这样做通常是没有必要,除非该交易在其他线程工作的依赖。

注意:您可以提交使用事务提交()只之前的活动保存其状态(当用户离开该活动)。如果试图在该点之后提交,一个异常将被抛出。这是因为,在提交之后的状态可以如果活动需要恢复丢失。对于其中的好,你失去的承诺的情况下,使用commitAllowingStateLoss() 

与活动通信


虽然一个片段被作为一个对象,该对象是独立于一实施 活动,并且可以内的多个活动中使用的,片段的给定实例是直接关系到包含它的活性。

具体来说,该片段可以访问活动与实例getActivity() ,并轻松地执行任务,如发现在活动布局视图:

查看的ListView =  getActivity () findViewByIdř ID 清单);

同样的,你的活动可以通过获取到的引用调用片段的方法 片段FragmentManager,使用findFragmentById()findFragmentByTag() 。例如:

ExampleFragment fragment =  ( ExampleFragment ) getFragmentManager (). findFragmentById ( R . id . example_fragment );

创建事件回调到活动

在某些情况下,你可能需要一个片段与大家分享的活动事件。要做到这一点的一个好方法是定义片段内的回调接口,并要求该主机活动实现它。当活动通过接口接收回调,它可以与根据需要在布局其它片段的信息。

例如,如果一个新闻应用程序有一个活动,一个显示的文章(片段A)的列表两个片段,另一个显示,当一个列表项被选中-然后片段A必须告诉活动,这样的文章(片段B)它可以告诉片段B到显示文章。在这种情况下,OnArticleSelectedListener接口声明片段A内:

公共 静态  碎裂 扩展 ListFragment  {     ...     //集装箱活动必须实现此接口    的公共 接口 OnArticleSelectedListener  {         公共 无效onArticleSelected 乌里articleUri );     }     ... }

然后,承载片段活动实现了OnArticleSelectedListener 接口,并覆盖onArticleSelected()通知从片段A.事件的碎片B为确保主机活动实现了这个接口,片段A的onAttach()回调方法(该系统调用时加入片段的活性)实例的实例OnArticleSelectedListener由铸造活动传递到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 ()  +  "必须实现OnArticleSelectedListener“ );         }     }     ... }

如果活动没有实现的接口,然后将片段抛出一个 ClassCastException异常。成功时,mListener成员包含到活动的实施参考 OnArticleSelectedListener,让碎片A可以通过调用由定义的方法分享活动事件OnArticleSelectedListener接口。例如,如果片段A是的延伸ListFragment,每次用户点击列表项,系统调用onListItemClick()中的片段,然后调用onArticleSelected()一起分享该活动的事件:

public  static  class  FragmentA  extends  ListFragment  {     OnArticleSelectedListener mListener ;     ...     @Override     public  void onListItemClick ( ListView l ,  View v ,  int position ,  long id )  {         //追加与内容提供商的URI单击项目的行ID         乌里noteUri =  ContentUris withAppendedIdArticleColumns CONTENT_URI ID );         //发送事件和URI主机活动        mListener onArticleSelected noteUri );     }     ... }

ID传递给参数onListItemClick()是被点击的项目,该活动(或其他片段)利用来从应用程序的文章的行ID的ContentProvider

关于使用内容提供商的更多信息中获得内容提供商的文档。

将项目添加到应用程序栏

您的片段可以促进菜单项到活动的选项菜单(,因此,在应用栏通过实施) onCreateOptionsMenu() 。为了让这个方法来接听电话,但是,你必须调用setHasOptionsMenu()的onCreate() ,以表明该片段想将项目添加到选项菜单(否则,片段将不会接收到呼叫 onCreateOptionsMenu())。

任何项目,你然后从片段添加到选项菜单被附加到现有的菜单项。该片段还接收回调onOptionsItemSelected()当选择了菜单项。

您还可以注册在你的片段布局的视图通过调用提供上下文菜单registerForContextMenu() 。当用户打开上下文菜单中,该片段接收到呼叫onCreateContextMenu() 。当用户选择一个项目时,片段接收到呼叫onContextItemSelected() 

注意:虽然您的片段接收它增加了每个菜单项的上选择的项目回调,活性是第一,当用户选择一个菜单项以接收各自的回调。如果活动的实现上的项目选择的回调不处理所选择的项目,则该事件被传递到片段的回调。这是选项菜单和上下文菜单如此。

有关菜单的详细信息,请参阅菜单开发指南和应用栏培训班。

处理片段生命周期


图3.关于该片段的生命周期的活动周期的影响。

管理片段的生命周期是诸如管理活动的生命周期了很多。像的活性,片段可以在三种状态存在:

恢复
该片段是在运行活动可见。
已暂停
另一项活动是在前台和具有焦点,但在此生活片段的活性仍然可见(前台活动是部分透明或不覆盖整个屏幕)。
已停止
该片段是不可见的。任一主机活动已经停止或片段已从活动删除,但加入到背面栈。停止的片段是还活着(所有状态和成员信息被系统保留)。然而,它不再对用户可见,并且如果活动被杀死会被杀死。

另外像一个活动,您可以保留使用一个片段的状态捆绑,以防活动的过程中被杀害,你需要在活动重新恢复片段的状态。您可以在片段的过程中保存状态的onSaveInstanceState()回调过程中,可以恢复它的onCreate() onCreateView() ,或onActivityCreated() 。有关保存状态的更多信息,请参见活动 文档。

在生命周期的活性和片段之间的最显著差异是一个被如何存储在其各自的背部栈。活性被放置成是由该系统时,它的停止管理活动的背栈,由缺省值(使得用户可以与导航回 ​​到它后退按钮,如在讨论任务和返回堆栈)。然而,一个片段放入主机管理活动,只有当你明确要求该实例通过调用保存在栈回addToBackStack()的事务,消除片段中。

否则,管理片段的生命周期很相似管理活动的生命周期。因此,对于相同的做法管理活动的生命周期也适用于片段。你还需要了解,虽然是活动的生活是如何影响片段的生活。

注意:如果你需要一个上下文的内对象碎片,就可以调用getActivity() 。但是,要小心调用getActivity()只有当片段连接到一个活动。当该片段尚未连接,或者其生命周期,年底期间分离getActivity()将返回null。

与该活动的生命周期协调

在该片段住直接活动的生命周期影响片段的生命周期,使得每个生命周期回调用于在类似的回调为每个片段的活性的结果。例如,当活动接收的onPause() ,在活动的每个片断接收的onPause() 

片段具有一些额外的生命周期回调,但是,处理以执行诸如生成动作和破坏片段的UI与活动独特相互作用。这些额外的回调方法有:

onAttach()
当该片段已用活性(关联称为活性在这里传递)。
onCreateView()
调用,以创建与片段相关联的视图层次。
onActivityCreated()
当活动的所谓的onCreate()方法返回。
onDestroyView()
与片段相关联的视图层次被移除时调用。
onDetach()
当该片段被从活动解除关联调用。

一个片段的生命周期的流动,因为它是由它的主机活动的影响,是通过图3在该图中示出,可以看到活动的每个连续状态如何确定回调方法的片段可以接收。例如,当活动已收到其的onCreate()回调,在活性的片段没有收到不是更onActivityCreated()回调。

一旦活性达到续状态,可以自由添加和删除片段的活性。因此,只有当活动是在重新开始的状态可以在片段的生命周期独立地发生变化。

然而,当活动叶续状态,该片段再次通过其生命周期的活动推。


为了使本文件中一并讨论的一切,这里是用两个片段创建一个双窗格布局的活动的一个例子。下面的活动包括一个片段展现莎士比亚的播放列表中标题和从列表中选择当另一个展现该剧的摘要。它还演示了如何提供片段的不同的配置,是根据画面构成。

注:这项活动的完整源代码可在 FragmentLayout.java

主活动适用期间以通常的方式布局,的onCreate() 

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

应用的布局是fragment_layout.xml

<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.example.android.apis.app.FragmentLayout$TitlesFragment"             android:id = "@+id/titles"  android:layout_weight = "1"             android:layout_width = "0px"  android:layout_height = "match_parent"  />     <FrameLayout  android:id = "@+id/details"  android:layout_weight = "1"             android:layout_width = "0px"  android:layout_height = "match_parent"             android:background = "?android:attr/detailsElementBackground"  /> </LinearLayout>

采用这种布局,系统实例化TitlesFragment(其列出了播放标题)只要活动加载布局,而的FrameLayout (其中为示出播放摘要将去片段)在屏幕的右侧消耗空间,但一开始是空的。正如你将看到下面,它不是,直到用户选择从一个片段放入列表中的项目的FrameLayout

然而,并非所有的屏幕配置的宽度足以同时显示由侧的戏剧的列表和摘要,侧。所以,上面的布局仅用于风景屏幕配置,通过在保存RES /布局脊/ fragment_layout.xml

因此,当屏幕处于纵向,系统将应用以下布局,这是保存在RES /布局/ fragment_layout.xml

<FrameLayout  xmlns:android = "http://schemas.android.com/apk/res/android"     android:layout_width = "match_parent"  android:layout_height = "match_parent" >     <fragment  class = "com.example.android.apis.app.FragmentLayout$TitlesFragment"             android:id = "@+id/titles"             android:layout_width = "match_parent"  android:layout_height = "match_parent"  /> </FrameLayout>

这种布局只包括TitlesFragment。这意味着,当该装置处于直式定向,只播放标题的列表是可见的。这样,当用户在该构造点击列表项,应用程序将开始一个新的活动,以显示,而不是加载的第二片段的摘要。

接下来,你可以看到它是如何在片段类来完成的。首先是TitlesFragment,这显示了莎士比亚的戏剧标题列表。该片段扩展ListFragment并依靠它来处理大部分的列表视图的工作。

当你检查这些代码,请注意,有两种可能的行为,当用户点击列表项:这取决于两个布局被激活,它可以创建并显示一个新片段显示在同一个活动的详细信息(添加该片段到的FrameLayout),或开始新的活性(其中,可被显示的片段)。

                                         填充表与我们的静态数组           检查是否我们在其中嵌入细节的框架        //片段直接在含                                    恢复托运位置最后的状态。            mCurCheckPosition = savedInstanceState 调用getInt “curChoice”  0 );         }         如果 mDualPane  {             //在双窗格模式,列表视图中突出显示选定的项目。            getListView ()。setChoiceMode ListView控件CHOICE_MODE_SINGLE );             //确保我们的UI是正确的                                                             辅助函数或者通过以显示所选择的项目的详细信息,     *在当前的UI显示在就地的片段,或启动一个     在其中显示*整个新的活动。     * /     空隙showDetails INT 指数 {         mCurCheckPosition = 索引;         如果 mDualPane  {             //我们可以显示就地与片段一切,所以更新            //列表以高亮显示所选项目并显示数据            getListView ()。setItemChecked 指数 真正的);             //检查哪些片段目前所示,如果更换                                               做出新的片段显示此选项。                细节=  DetailsFragment 的newInstance 指数);                 //执行事务,替换任何现有的片段,                //使用这个里面的                                                                                                        否则,我们需要推出新的活动,以显示            //对话片段与选定                           

第二个片段,DetailsFragment显示了从列表中选择的项目剧中总结TitlesFragment

公共 静态  DetailsFragment  扩展 片段 {     / **      *创建DetailsFragment的新实例,初始化为     *显示在“索引”的文本。     * /     公共 静态 DetailsFragment 的newInstance INT 指数 {         DetailsFragment ˚F =   DetailsFragment ();         //供应索引输入作为                                                                                        我们有不同的布局,并在其中一本            //片段的含框架不存在。片段            //仍可以从保存的状态创建的,但            //没有理由尝试创建视图的层次结构,因为它            //将不会显示。请注意,这是不需要的-我们可以            //只运行下面的代码,我们将创建并返回            //视图层次; 它只是永远不会                                                                              

从召回TitlesFragment类,即,如果用户点击列表项和当前布局并包括R.id.details视图(也就是,其中DetailsFragment所属),则应用程序启动DetailsActivity 活性要显示的内容的项目。

这里是DetailsActivity,它只是嵌入DetailsFragment当屏幕处于纵向显示所选播放总结:

public  static  class  DetailsActivity  extends  Activity  {     @Override     protected  void onCreate ( Bundle savedInstanceState )  {         super . onCreate ( savedInstanceState );         if  ( getResources (). getConfiguration (). orientation                 ==  Configuration . ORIENTATION_LANDSCAPE )  {             //如果现在屏幕处于横向模式,就可以显示            //对话框中,符合列表,这样,我们就不需要这一活动。            面漆();             回报;         }         如果 savedInstanceState ==   {             //在最初的设置,插上细节fragment.             DetailsFragment details =  new  DetailsFragment ();             details . setArguments ( getIntent (). getExtras ());             getFragmentManager (). beginTransaction (). add ( android . R . id . content , details ). commit ();         }     } }

注意,该活动完成本身如果配置为横向,从而使主活动可以接管并显示DetailsFragment沿着TitlesFragment。如果用户开始这可能会发生DetailsActivity而在纵向,但随后旋转到横向(它重新启动当前活动)。

对于使用片段(和完整的源文件,在这个例子中)更多的样本,请参阅可用的API演示示例应用程序ApiDemos(可从下载样本SDK组件)。


1 0
原创粉丝点击