fragments

来源:互联网 发布:linux怎么查看版本 编辑:程序博客网 时间:2024/05/22 13:14

Fragments

片段

Quickview

  • Fragments decompose application functionality and UI into reusable modules
  • Add multiple fragments to a screen to avoid switching activities
  • Fragments have their own lifecycle, state, and back stack
  • Fragments require API Level "Honeycomb" or greater

In this document

  1. Design Philosophy
  2. Creating a Fragment
    1. Adding a user interface
    2. Adding a fragment to an activity
  3. Managing Fragments
  4. Performing Fragment Transactions
  5. Communicating with the Activity
    1. Creating event callbacks to the activity
    2. Adding items to the Action Bar
  6. Handling the Fragment Lifecycle
    1. Coordinating with the activity lifecycle
  7. Example

Key classes

  1. Fragment
  2. FragmentManager
  3. FragmentTransaction

Related samples

  1. ApiDemos

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.

一个片段是指在一个activity里用户界面的某一个部分或者是某一种行为。你可以在一个单独的activity里将很多个片段联合在一起,组成一个多窗口的用户界面,也可以在很多个activity里复用同一个片段。你可以把一个fragment看作是activity的一个模块化的组成部分,它有自己的生命周期,接收它自己的输入事件,而且在activity运行的时候还可以增加或者删除。

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

一个fragment必须总是嵌入在一个activity里,它的生命周期直接受到宿主activity的生命周期的影响。比如,当宿主activity被暂停,此activity里所有的fragment都被暂停;当宿主activity被销毁,其中所有的fragment也被销毁。但是,当一个activity运行时(在生命周期里处于resume状态),你可以独立的操纵其中的每一个fragment,比如增加,删除。当你对某个fragment作这些状态的转换时,你同样可以将它压入activity管理的活动栈——activity里每一个活动栈的条目是一个关于fragment状态变换的记录。活动栈允许用户通过按BACK键来回退一个fragment转换(导航的回退键)。

When you add a fragment as a part of your activity layout, it lives in a ViewGroup inside the activity's view hierarchy and defines its own layout of views. 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 as an invisible worker for the activity.

当你增加一个fragment,作为你的activity布局的一部分时,它位于activity的view层次体系的ViewGroup里,指定它自己的views的布局。想在你的activity布局里插入一个 fragment,你可以在activity的layout文件里声明这个fragment,作为一个<fragment>元素,或者你也可以在应用程序代码里将它加入到一个已经存在的ViewGroup。不过,fragment并没有被规定一定要作为activity布局的一部分,你同样可以将它作为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.

这篇文档描述了怎样在你的应用程序里使用fragment,包括当fragment被添到activity的活动栈时,它怎么维护自己的状态信息的,与本身的activity或者其他的fragment共享事件,如何在activity的行动条里发挥作用,等等。

Design Philosophy

设计理念

Android introduced fragments in Android 3.0 (API Level "Honeycomb"), 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 mobile phone, 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.

Android从Android 3.0(API “蜂蜜”)以后引入了fragment,主要为了在大屏幕上提供更有效率的和更灵活的UI设计,比如平板。由于平板的屏幕比手机大的多,在平板上有更多得空间来联合以及变化这些UI元素。fragment这样的设计理念,让你不必要再去管理view层次体系的各种复杂变化。通过将activity的布局分割成几个fragment,你可以在运行时修改一个activity的外观,还可以通过此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 figure 1.

比如,一个新闻应用程序可能使用一个在左边的fragment来展示一列文章列表,一个在右边的fragment来显示某篇文章的详细内容——这两个fragment相邻地出现在同一个activity里,每个fragment拥有它自己的生命周期回调函数的集合,而且处理它自己的用户输入事件。所以,之前需要使用一个activity来选择文章,另一个activity来读取这篇文章,现在用户可以在同一个activity里选择文章,读取文章。如图1描述的那样。


Figure 1. An example of how two UI modules that are typically separated into two activities can be combined into one activity, using fragments.

A fragment should be a modular and reusable component in your application. That is, because the fragment defines its own layout and its own behavior using its own lifecycle callbacks, you can include one fragment in multiple activities. This is especially important because it allows you to adapt your user experience to different screen sizes. For instance, you might include multiple fragments in an activity only when the screen size is sufficiently large, and, when it is not, launch separate activities that use different fragments.

在你的应用程序中,fragment应该是模块化的、可复用的。因为,fragment自己指定自己的布局,自己指定自己的行为,使用的是自己的生命周期回调函数。故你可以将一个fragment应用于多个activity。这极其的重要,因为使用这种模式,你可以在不同大小的屏幕上获得相同的用户体验。例如,你也许会在一个activity里包含很多的fragment,只有在屏幕足够大的情况下;当屏幕并没有那么大时,运行很多个activity,让每个activity去使用不同的fragment。

For example—to continue with the news application example—the application can embed two fragments in Activity A, when running on an extra large screen (a tablet, for example). However, on a normal-sized screen (a phone, for example), there's not be 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 fragment to read the article. Thus, the application supports both design patterns suggested in figure 1.

例如——继续使用上面新闻的例子——应用程序可以在Activity A里嵌入两个fragment,当运行在一个非常大的屏幕上(比如,平板)时。但是,在一个普通大小(比如,手机)上时,没有放下两个fragment的空间。所以,Activity A只能包含一个显示文章列表的fragment,然后当用户选中某一个article,它启动Activity B,B中包含了读取文章内容的fragment。这样,应用程序支持了图1的两种设计样式。

Creating a Fragment

创建一个Fragment



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

To create a fragment, you must create a subclass of Fragment (or an existing subclass of it). The Fragment class has code that looks a lot like an Activity. It contains callback methods similar to an activity, such as onCreate()onStart()onPause(), andonStop(). 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.

要创建一个fragment,首先应该创建Fragment(或者它已经存在的一个子类)的一个子类。Fragment类的代码类似Activity。它包含类似于activity的回调函数,如onCreate(),onStart(),onPause()和onStop()。事实上,如果你要将一个已经存在的Android应用转换为使用fragment,你也许只需要简单的从你activity的回调函数里取出代码移动到你的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.
系统创建fragment的时候调用它。在你的实现中,你应该初始化fragment中那些必要的组件,当fragment被暂停或停止,然后恢复之后,你想保持着的那些组件。
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.
当fragment第一次绘制它的用户界面时,系统调用它。想绘制一个用户界面,你必须从这个方法返回一个View,它是你的fragment的布局的根View。如果fragment不提供UI,返回一个null。
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).
用户第一次有迹象离开fragment时,系统调用这个方法(但是这不总是意味着此fragment即将被销毁)。通常在这个方法里,你应该提交所有的在当前会话之前的应该保持下来的变动信息(因为用户可能不会再回来)。

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 more later, in the section about Handling the Fragment Lifecycle.

大多数应用程序应该为每一个fragment实现至少这3种方法,但是其他的几个回调函数,你也可以使用,处理fragment生命周期的不同阶段。所有的生命周期回调函数在之后有更详细的讨论,在Handling the Fragment Lifecyle部分。

There are also a few subclasses that you might want to extend, instead of the base Fragment class:

除了Fragment基类,还有几个子类也许你会想要扩展:

DialogFragment
Displays a floating dialog. Using this class to create a dialog is a good alternative to using the dialog helper methods in theActivity 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.
显示一个浮动对话框。相比于在Activity类里使用对话框助手那个方法,使用这个类来创建对话框将会是个好的变化,因为你可以将一个对话框fragment并入由activity管理的fragment活动栈,从而可以让用户返回到一个隐藏的fragment。
ListFragment
Displays a list of items that are managed by an adapter (such as a SimpleCursorAdapter), similar to ListActivity. It provides several methods for managing a list view, such as the onListItemClick() callback to handle click events.
显示由一个adapter(比如SimpleCursorAdapter)管理的一列项目,类似ListActivity。它提供了几个管理列表的方法,比如onListItemClick()来处理单击事件。
PreferenceFragment
Displays a hierarchy of Preference objects as a list, similar to PreferenceActivity. This is useful when creating a "settings" activity for your application.
将一个Preference对象的层次结构显示为一个列表的形状,类似PreferenceActivity。在你的应用创建一个“设置”activity时有用。

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.

fragment通常作为activity用户界面的一部分,把它自己的布局融入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.

为一个fragment提供一个布局,你必须实现onCreateView()回调函数,当fragment需要绘制它的布局时,Android系统就会调用这个函数。你关于这个函数的实现,必须返回一个View,而且必须是你的fragment布局的根。

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

注意:如果你的fragment是ListFragment的一个子类,默认的实现将从onCreateView()返回一个ListView,所以你不需要再实现它。

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.

要从onCreateView()返回一个布局,你可以在布局资源文件里(使用XML定义)完成布局。为帮助你返回这个布局,onCreateView()提供了一个LayoutInflator对象。

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

例如,下面是Fragment的一个子类,从example_fragment.xml里载入一个布局:

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 (restoring state is discussed more in the section about Handling the Fragment Lifecycle).

传递给onCreateView()方法里的container参数是来自于activity布局的ViewGroup,你的fragment布局就将会插入到它里面。savedinstanceState参数是一个Bundle,提供之前的fragment实例的一些数据,如果fragment是被恢复的。

The inflate() method takes three arguments:

inflate()方法带三个参数:

  • 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.)
  • 你想添加的布局的资源ID
  • 被添加的布局的上层,ViewGroup。传递container这个参数,系统才能应用布局参数到被添加的布局的根view里,被添加的布局就是由上层的view指定的。

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

现在你已经知道怎么创建一个布局好的fragment,下一步,你应该知道怎么在activity里加入你的fragment。

Adding a fragment to an activity

在activity里增加一个fragment

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:

通常,一个fragment向宿主activity贡献一部分UI,作为activity的整个view层次体系的一部分嵌入到activity。有两种方法向activity布局加入一个fragment:

  • Declare the fragment inside the activity's layout file.
  • 在activity的布局文件里声明fragment

    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:

    这种情况下,你可以把一个fragment当成一个view一样来指定它的布局属性。例如,下面是一个有两个fragment的activity的布局文件:

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

    在上面的布局里fragment里的android:name属性描述了Fragment类的实例。

    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.

    当系统创建这个activity的布局时,它实例化布局里的每个fragment,然后为每一个调用onCreateView()方法,来重新得到每个fragment的布局。系统直接在<fragment>元素对应的位置插入fragment返回的View。

    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:

    注意:每个fragment需要一个唯一的标识符,然后系统才可以在activity重启的时候,使用它来重新构建fragment(也可以用它来抓取这个fragment,去完成某些操作,比如删除它)。给一个fragment提供ID有3种方法:

    • 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.
    • 使用唯一的ID来给android:id赋值
    • 使用唯一的字符串给android:tag赋值
    • 如果你上面两个都没做,系统使用container view的ID标识它。

  • Or, programmatically add the fragment to an existing ViewGroup.
  • 或者,从程序里向一个已有的ViewGroup里加入fragment:

    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.

    在你的activity运行的任意时刻,你都可以向activity布局里加入fragment。你只是要指定一个ViewGroup来放置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 ofFragmentTransaction from your Activity like this:

    要使你的activity里的fragment状态改变(比如增加,删除,或者替换一个fragment),你必须使用FragmentTransaction里的API。你可以像下面这样从你的activity里获得一个FragmentTransaction的实例。

    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:

    然后你可以使用add()方法来增加一个fragment,指定增加哪个fragment,以及它要被插入到哪个view。例如:

    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.

    add()方法的第一个参数,是ViewGroup,fragment应该被放置的地方,由它的资源ID来指定。第二个参数是想要增加的fragment。

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

    一旦你已经使用FragmentTransaction做了改变,你就必须使用commit()来让变化生效。

Adding a fragment without a UI

增加一个没有界面的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.

上面的例子展示了在你的activity里,怎样增加一个fragment来提供一个UI。你还可以使用fragment提供一个后台行为,而不提供额外的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.

要增加一个没有UI的fragment,使用add(Fragment,String)方法来从activity里增加一个fragment(给fragment提供一个唯一的字符串tag,而不是一个view ID)。这增加了一个fragment,但是由于它没有与activity布局里的任一个view建立联系,你不会收到调用onCreateView()的命令的。所以你不需要实现这个方法。

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

给fragment提供一个字符串标志并不仅限于无UI的fragment——你同样可以给有UI的fragment提供字符串标志——但是如果fragment没有UI,那么字符串标志就是唯一的标识这个fragment的方法。如果你在activity的后面想得到这个fragment,你需要使用findFragmentByTag()。

For an example activity that uses a fragment as a background worker, without a UI, see the FragmentRetainInstance.java sample.

想看看使用fragment作为后台工作的activity例子,看看例子FragmentRetainInstance.java。

Managing Fragments

管理Fragments

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

管理你的activity里的fragment,你需要使用FragmentManager。在activity里调用getFragmentManager()来得到FragmentManager。

Some things that you can do with FragmentManager include:

使用FragmentManager可以做的事包括:

  • 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().
  • 获得activity里存在的fragment,使用findFragmentById()(对于那些提供了UI的fragment)或者findFragmentByTag()(对于那些没有提供UI的fragment)。
  • 从活动栈里弹出fragment,使用popBackStack()(模拟用户的BACK命令)。
  • 注册一个活动栈变化的监听,使用addOnBackStackChangedListener()。

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

要获得关于这些方法和其他的更多信息,参看FragmengManager类的文档。

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.

如同之前部分描述的一样,你同样可以使用FragmentManager来打开一个FragmentTransaction,完成一些事务,比如增加和删除fragment。

Performing Fragment Transactions

完成Fragment状态转换

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

在activity里使用fragment的一个重要特性是:可以增加、删除、替换或者其他动作,根据用户的行为。每组你提交给activity的变化称为一个事务,你可以使用FragmentTrasaction的API来完成一个事务。你也可以把每个事务保存到activity管理的活动栈里。通过活动栈,用户可以导航回退这些fragment变化。(与activity导航回退类似。)

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

你可以像下面这样从FragmentManager来获得一个FragmentTrasaction的实例:

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

每个事务是一组你想在同一时间完成的一组变化。你可以在一个事务中完成所有你想要完成的变化,比如add(),remove(),replace().然后,应用这个事务到activity,你必须调用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 key.

在调用commit()之前,也许你会想调用addToBackStack(),把事务添加到一个fragment事务的栈中。这个事务栈由activity来管理,允许用户通过按BACK键来回到先前的fragment状态。

For example, here's how you can replace one fragment with another, and preserve the previous state in the back stack:

例如,下面说明了你可以怎么用一个fragment替换另一个,然后保存先前的状态到事务栈:

// 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 BACK key.

这个例子中,newFragment替代了由R.id.fragment_container指定的当前布局容器的fragment。通过调用addToBackStack(),这个替换事务保存到事务栈,以让用户回退事务,通过按BACK键来回到先前的fragment。

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

如果你在某事务中增加了很多的变化,(比如又一个add()或remove()),然后调用addToBackStack(),在你commit()之前的所有的变化都会作为一个事务添加到事务栈,用户的一个BACK键将会回退它们所有的变化。

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

你增加变化到FragmentTransaction的次序并不重要,除了:

  • 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
  • 你必须最后调用commit()
  • 如果你增加很多fragment到同一个container,那么你增加它们的顺序决定了他们在view的层次体系中出现的顺序。

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.

如果你在完成删除一个fragment的事务操作时,没有调用addToBackStack(),那么当事务被提交时这个fragment被销毁了,用户就不能导航回退到它。如果你在删除fragment的是后调用了addToBackStack(),那么fragment被停止(而不被销毁),当用户导航回退时,它就会恢复了。

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

提示:对于每个fragment事务,你可以申请一个事务动画,在提交之前调用setTransition().???不懂

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 call executePendingTransactions() from your UI thread to immediately execute transactions submitted by commit(). Doing so is usually not necessary unless the transaction is a dependency for jobs in other threads.

调用commit()时并不会立即完成事务。实际上,它将事务安排到activity的UI线程(主线程)上运行,一当线程能这样做的时候。如果需要,你可以从你的UI线程里调用executePendingTransactions()来立刻执行commit()提交的事务。一般不必要这样做,除非其他线程的工作依赖这个事务。

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, usecommitAllowingStateLoss().

警告:使用commit()提交事务必须要先于activity的保存自己状态(当用户离开activity时发生)。如果你在那个点之后试图提交事务,会抛出一个异常。因为提交之后的那个状态可能会丢失,如果activity之后需要恢复自己。对于那些丢失commit没有太大关系的情况,请使用commitAllowingStateLoss()。

Communicating with the Activity

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

尽管Fragment作为一个独立于Activity的对象被实现,而且可以在很多的activity里使用,一个给定的fragment的实例是直接绑定到包含它的activity里的。

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

特殊地,fragment可以访问Activity的实例,用getActivity()方法,而且用它轻松完成各种动作,比如找到一个activity布局的view。

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

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

类似地,你的activity可以通过从FragmentManager索取Fragment的一个引用,使用findFragmentById()或者findFragmengByTag(),然后可调用fragment里的方法。例如:

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

Creating event callbacks to the activity

创建到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.

在某些情况下,你也许要求一个fragment和activity共享事件。实现这个的一个好方法是:在fragment里指定一个回调接口,然后要求宿主activity来实现它。当activity通过接口接收到一个回调,它可以与布局里的其他有需要的fragment共享信息。

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:

例如,如果一个新闻应用的activity里有两个fragment——一个显示文章列表(fragment A),另一个显示文章内容(fragment B)——然后fragment A必须告诉activity何时一个列表项目被选中,这样它才能告诉fragment B来显示那篇文章。这种情况下,onArticleSelectedListener接口就应该在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 ofOnArticleSelectedListener by casting the Activity that is passed into onAttach():

然后作为fragment宿主的activity实现onArticleSelectedListener接口,覆盖了onArticleSelected()方法,将fragment A的事件通知fragment B。为了保证宿主activity实现这个接口,fragment A的onAttach()回调函数(系统会在增加fragment到activity的时候调用它)例示OnArticleSelectedListener——它强制转换Activity为它的一个实例,而Activity是之前系统传递给onAttaach()的。

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 ofOnArticleSelectedListener, so that fragment A can share events with the activity by calling methods defined by the OnArticleSelectedListener interface. 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:

如果activity没有实现这个接口,那么fragment会抛出一个类强制转换异常。如果实现了,mListener变量就是activity里实现的onArticleSelectedListener类的一个引用,因此fragment A可以通过OnArticleSelectedListener接口中定义的方法来与activity共享事件。例如,如果fragment A是ListFragment的一个扩展,每次用户点击一个列表项目时,系统调用fragment里的onListItemClick()方法,然后这方法里会调用onArticleSelected()而与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.

传递给onListItemClick()的id参数是选中的项目的列ID,activity(或者fragment)用它来从应用程序的ContentProvider取得文章内容。

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

更多关于怎么使用content provider的,请看Content Provider文档。

Adding items to the Action Bar

向Action Bar里增加项目

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


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 calling registerForContextMenu(). When the user opens the context menu, the fragment receives a call toonCreateContextMenu(). When the user selects an item, the fragment receives a call to onContextItemSelected().

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 Creating Menus and Using the Action Bar.

Handling the Fragment Lifecycle



Figure 3. The activity lifecycle's affect 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 key, 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 formanaging 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.

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 receivesonPause(), 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 at res/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();                ft.replace(R.id.details, 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 from TitlesFragment:

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 the DetailsActivity 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 the DetailsActivity 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 sample code available in ApiDemos (available for download from the Samples SDK component).