第一次发布翻译,自己翻译水平有限,若有错漏,一定多多指教,互相进步!

翻译原文网址:http://developer.android.com/guide/components/fragments.html

Fragment

一个Fragment代表着Activity的一种行为或其界面的一部分。你可以将多个Fragement组建在一个Activity来构建一个多窗口的UI,并且复用fragment在多个Activity中。你可以把fragment当作是Activity的一个模块,它拥有自己的生命周期,接收自己的输入事件。并且Activity可以在运行时添加或移除这个模块(类似于一个子类Activity,你可以复用在不同的Activity中)



一个fragment总是被嵌入在一个Activity中,并且这个fragment的生命周期直接被宿主Activity的生命周期影响。例如,当这个Activity的生命周期暂停,所有其中的fragment也是如此。当这个Activity的生命周期销毁时,所有其中的fragment也是如此。但是,当一个Activity正在运行时(生命周期处在onresume),你可以独立操作每个片段,比如添加或删除它。当你执行这样一个fragment事务时,你也可以添加fragment到Activity管理的回退栈中——Activity中每个回退栈的入口是这个fragment事务的一个记录。这个回退栈允许用户回退一个fragment事务(向后导航),通过按回退按钮。

当你添加一个fragment作为你的Activity布局layout的一部分,它存在于这个Activity的视图层次内的ViewGroup中,并且这个fragment定义了自己的视图布局。你可以通过在Activity中声明fragment的布局文件插入一个fragment到Activity中,标签。或者从你的应用代码中添加Fragment到一个已存在的ViewGroup。但是,一个Fragment不强求成为Activity布局的一部分;你也可以使用一个没有界面的Fragment作为Activity中一个看不见的工作者。
这个文档描述了如何使用Fragment构建你的应用,包括当Fragment添加到Activity回退栈中时,如何保持自己的状态;Activity与Activity中其他的Fragment共享事件,有助于这个Activity的action bar,或更多。
一、设计理念 Android介绍Fragment在Android 3.0(API level 11),主要是为了在大屏幕上更多动态和灵活的UI设计,例如平板电脑。因为平板电脑的屏幕比手持设备大的多。这有更多的空间来组合和互换UI控件。Fragment允许这样的设计却不需要你管理视图层次复杂的变化。通过将Activity的布局划分成Fragment,你可以在运行时修改Activity的视图并保存这些改变在Activity管理的回退栈中。
比如,一个消息应用可以在左边使用Fragment展示文章的列表,在右边用Fragment来展示文章的内容。——两种Fragment并排显示在一个Activity中,每个Fragment拥有自己的一组生命周期回调方法,和处理它们自己的输入事件。而不是使用一个Activity来选择文章,使用另一个Activity来阅读文章。用户可以选择一篇文章和阅读一篇文章在同一个Activity中。例如图1中显示的平板布局。
你应该设计每个Fragment作为一个模块和可复用的Activity组件。也就是说,因为每个Fragment定义它自己的布局,并且自己的生命周期内有自己的行为活动,你可以在多个Activity中使用Fragment,所以你设计时应该具有可复用性,并且避免直接的从一个Fragment操作另一个Fragment。这一点尤为重要,因为模块化的Fragment允许你当针对不同屏幕尺寸时,改变你的Fragment组合。当设计应用同时支持手机和平板时,你可以在不同的布局配置中复用你的Fragment来优化基于所需屏幕空间的用户体验。例如,在手持设备,当多个Fragment不适合在同一个Activity中时,它可能需要分离Fragment来提供一个单窗口的UI。 图1. 一个例子说明在平板设计中两个被Fragment定义的UI模块如何组合在一个Activity中,而不是像手持设备中那样分离开。
例如,继续拿新闻类应用做例子,应用可以在Activity A中嵌入两个Fragment,当运行在平板大小的设备上时。但是,一个手持大小的屏幕,这就没有足够的空间承载两个Fragment,所以Activity A包含一个Fragment来显示文章列表,当用户选择文章列表中的文章时,它启动Activity B,Activity B包含第二个Fragment来显示文章内容。因此,应用通过不同的组合复用Fragment来支持平板设备和手持设备。就像图1.
关于针对不同的屏幕配置使用不同的Fragment组合来设计你的应用的更多信息,请查看  Supporting Tablets and Handsets.
二、创建一个Fragment

图2. fragment的生命周期(当它的Activity正在运行时)
要创建一个Fragment,你必须创建一个Fragment的子类(或者一个已存在的子类)。Fragment类拥有的代码看起来很像Activity。它包含的回调方法类似Activity,比如onCreate(),onStart(),onPause(),以及onStop()。事实上,如果你使用Fragment转化一个已存在的应用,你可能从你的Activity的回调方法简单的移动代码到你的Fragment的回调方法。
通常,你需要实现至少下面的回调方法:
onCreate():当创建Fragment时,系统调用这个方法。当你实现这个方法,你应该初始化Fragment的基本的组件,这些组件是你想要保留的,当Fragment被暂停(pause)或停止(stop),然后恢复(resume)时。
onCreateView():当Fragment第一次绘制自己的界面的时候,系统调用这个方法。为了给你的Fragment绘制一个UI,你必须从这个方法返回一个View,这就是你的Fragment布局的根。你可以返回null,如果你的Fragment不需要UI时。
onPause():当第一次表明用户正在离开Fragment时(虽然它并不总是意味着Fragment正在被销毁),系统调用。这里通常是你应该提交一些持续化的更改(因为可能用户不再回来)。
对于每个Fragment,大部分应用应该实现至少这三种方法,但是这还有其他的回调方法你也应该用来处理Fragment生命周期的不同阶段。所有的生命周期回调方法被详细描述在 Handling the Fragment Lifecycle.
这有一些子类你可能想要继承,而不是仅仅使用Fragment基础类:
DialogFragment: 显示一个浮动的对话框。使用这个类来创建一个对话框是一个好的替代,相比于在Activity中使用dialog Helper的方法。因为你可以把DialogFragment合并进Activity管理的Fragment的回退栈中,允许用户返回到这个已经消失的DialogFragment。
ListFragment: 显示一个列表,items被adapter(例如SimpleCursorAdapter)管理,类似于ListActivity。它提供了一些方法来管理一个list view。例如onListItemClick()处理点击事件。
PreferenceFragment: 显示选项列表。类似于PreferenceActivity(android 3.0后已不介意使用)。这个类是非常有用的当给你的应用创建“设置”Activity时。



三、添加一个界面 一个Fragment通常用来作为一个Activity界面的一部分,并拥有自己的布局。
为了给Fragment提供布局,你必须实现onCreateView()回调方法。当Fragment要绘制自己的布局时,系统调用onCreateView()。你实现这个方法必须返回一个View(可以为null)作为你Fragment布局的根。
注意:如果你的Fragment是ListFragment的子类,onCreateView()中默认返回ListView,所以你不需要实现这个方法。
为了从onCreateView()中返回一个布局,你可以填充(inflate)一个XML布局资源文件。为了辅助你做这件事,onCreateView()提供了一个LayoutInflater 类。
例如,这有个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);    }}
onCreateView()中的container参数是ViewGroup的父类(来自于Activity的布局),你的Fragment布局将会被插入到ViewGroup中。savedInstanceState参数是一个Bundle,它提供关于先前Fragment实例的数据,如果Fragment正在恢复(resume)(恢复状态更多的被讨论在 Handling the Fragment Lifecycle中)。
这个inflate()方法包含三个参数: 1.你想要填充的布局的资源ID 2. The  ViewGroup to be the parent of the inflated layout. Passing the  container is important in order for the system to apply layout parameters to the root view of the inflated layout, specified by the parent view in which it's going. ViewGroup是被填充布局的父布局。对于系统使用布局参数到已填充布局的根视图,传递container是重要的,它是由父视图指定。(无法理解这句话) 3.布尔值表明是否将填充布局(也就是第一个参数的布局)添加到ViewGroup(第二个参数)中。(在这个例子中是false是因为系统已将填充布局添加到container中——如果是true将会创建一个冗余的视图组在最终的布局中。)
通过源码可以看出:false——代表第一个参数布局中的属性值(如宽高的设置)可以设置到最后的布局中,也就是属性值起作用。并且第一个参数的布局不会添加到cantainer视图中。true——代表第一个参数布局中的属性值不起作用。并且第一个参数布局会添加到cantainer中。
现在你已经看见如何创建Fragment并提供布局。下一步,你需要添加Fragment到Activity中。
四、添加Fragment到Activity
通常,一个Fragment构成了宿主Activity界面的一部分,它被嵌入作为Activity整体视图层次的一部分。这里有两种方式你可以添加Fragment到Activity布局中: 1.在Activity布局文件中声明Fragment 在这个例子中,你可以为Fragment指定视图属性,就像声明一个View一样。例如,这有一个Activity有两个Fragment的布局文件:
<?xml version="1.0" encoding="utf-8"?> xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:layout_width="match_parent"    android:layout_height="match_parent">     android:name="com.example.news.ArticleListFragment"            android:id="@+id/list"            android:layout_weight="1"            android:layout_width="0dp"            android:layout_height="match_parent" />     android:name="com.example.news.ArticleReaderFragment"            android:id="@+id/viewer"            android:layout_weight="2"            android:layout_width="0dp"            android:layout_height="match_parent" />
android:name属性指定Fragment类在布局中初始化。
当系统创建这个Activity布局,它初始化每个被指定的Fragment并调用它们的onCreateView()方法,以检索每个Fragment的布局。系统插入onCreateView()中返回的视图,这视图通过Fragment直接取代元素。
注意:每个Fragment需要唯一标记,系统可以使用这个标记恢复指定的Fragmen,如果Activity重启(restart)(并且你可以使用这个标签捕获这个Fragment要执行的事务,比如移除它。)。这有三种方式给一个Fragment提供一个ID:
a.android:id属性指定一个唯一ID
b.android:tag属性指定一个唯一字符串
c.如果你没有提供以上两个,系统将使用容器视图的ID(the ID of the container view).
2.或者,在代码中添加Fragment到一个已存在的ViewGroup.
在你Activity运行的任意时刻,你都可以添加framents到你的Activity布局。你只需简单指定一个ViewGroup来显示Fragment。
在你的Activity提交一个Fragment事务(比如添加add,删除remove,或替代replace一个Fragment),你必须使用FragmentTransaction的APIs。你可以从你的Activity得到一个FragmentTransaction的实例。比如:
FragmentManager fragmentManager = getFragmentManager() ;FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
你可以添加一个Fragment使用add()方法,指定这个Fragment添加和插入的视图地方。例如:
ExampleFragment fragment = new ExampleFragment();fragmentTransaction.add(R.id.fragment_container, fragment);fragmentTransaction.commit();
add()第一个参数是ViewGroup,ViewGroup是Fragment显示的地方,ViewGroup被资源ID指定。第二个参数是要添加的Fragment。
一旦你通过FragmentTransaction做了些更改,你必须调用commit()使更改有效。
五、添加一个没有界面的Fragment
上述例子展示了如何添加一个Fragment到你的Activity以提供一个界面。但是,你也可以使用没有界面的Fragment给Activity提供一种后台行为。
添加一个无界面的Fragment,使用add(Fragment,String)(支持唯一的字符串“tag”对于Fragment而不是视图ID)添加Fragment到Activity。这就添加了Fragment,但是,因为它与Activity布局视图无关,它不会调用onCreateView().所以你不需要实现这个方法。
Fragment支持一个字符串标记tag不仅仅局限在无界面的Fragment—有界面的Fragment也支持—但是如果Fragment没有界面,字符串tag是辨识它的唯一方式。如果你后面想要从Activity得到Fragment,你需要使用findFragmentByTag()。
Activity使用没有界面的Fragment作为后台的工作者,请查看FragmentRetainInstance.java。在SDK例子中(可以通过SDK Manager获得),在你系统的路径:/APIDemos/app/src/main/java/com/example/android/apis/app/
六、管理Fragment
在你的Activity管理Fragment,你需要使用FragmentManager。从你的Activity调用getFragmentManager()可以得到它。
你可以使用FragmentManager做一些事情:
1.通过findFragmentById()(对于在Activity布局中的有界面的Fragment)或findFragmentByTag()(对于没有界面的Fragment)得到Activity中存在的Fragment。
2.从回退栈中弹出Fragment,使用popBackStack()(模拟用户使用回退命令)
3.注册一个回退栈变化的监听器,使用addOnBackStackChangedListener()
关于这些方法更多的信息,查看FragmentManager类文档。
上一节中的演示,你也可以使用FragmentManager打开一个FragmentTransaction, FragmentTransaction允许你执行事务,比如添加和删除Fragment。
七、执行Fragment事务
在你的Activity中使用Fragment一个很好地特点是可以添加,删除,替换,并使用它们执行其他的动作,在界面上相应。你在Activity提交的一系列更改叫做事务,你可以通过使用FragmentTransaction的APIs使用。你也可以保存事务到Activity管理的回退栈中,允许用户通过Fragment的更改导航回退(就像Activity导航回退一样)。
你可以从FragmentManager获得FragmentTransaction的一个实例,例如:
FragmentManager fragmentManager = getFragmentManager();FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每个事务是在同一时刻你想要执行的一组更改。你可以通过一个给定的事务装配你想要执行的所有的更改,使用add(),remove(),replace().然后应用事务到Activity,你必须使用commit().
但是在你commit()之前,为了添加事务到Fragment事务的回退栈中,你可能要调用addToBackStack().这个回退栈由Activity管理,并且允许用户通过返回键返回先前Fragment的状态。
这有个例子,演示如何用一个Fragment取代另一个,并且在回退栈中保存先前的状态:
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();FragmentTransaction transaction = getFragmentManager().beginTransaction();// Replace whatever is in the fragment_container view with this fragment,// and add the transaction to the back stacktransaction.replace(R.id.fragment_container, newFragment);transaction.addToBackStack(null);// Commit the transactiontransaction.commit();
在这个例子中,newFragment替代任意当前在布局容器中R.id.fragment_container的Fragment(如果存在。不存在则直接添加)。通过调用addToBackStack(),这个替代动作的事务被保存在回退栈,所以用户可以回退这个事务,并且返回先前的Fragment通过回退按钮。
如果你添加多个更改到一个事务中(比如add()和remove()),并调用addToBackStack(),然后在你调用commit()之前所有被应用的更改被作为单个事务添加到回退栈中。现在按回退键将会一起返回这些更改。
添加更改操作到FragmentTransaction与顺序无关,除了:
1.你必须最后调用commit()
2.如果你添加多个Fragment到同一个容器container中,你添加它们的顺序决定了它们在视图层次显示的顺序。
当你执行一个移除Fragment的事务,如果你不调用addToBackStack(),这个Fragment会销毁(destroy)当提交后。并且用户不能返回它。然而,当你移除Fragment时,如果你调用addToBackStack(),这个Fragment会停止(stop生命周期),并且恢复(resume)如果用户返回。
提示:每个Fragment事务,你可以应用一个事务动画,在提交之前通过调用setTransition()。
调用commit()不会立即执行事务。相反,它安排事务到activity的UI线程(“主”线程),线程才会执行。但是,如果需要立即执行,你应该从UI线程调用executePendingTransactions(),为了立即执行通过commit()提交的事务。这种做法通常是不需要的,除非该事务是其他线程的附属工作。
注意:你可以使用commit()提交一个事务,仅当Activity保存它的状态(当用户离开Activity)之前。如果你在这之后想要提交,将会抛出异常。这是因为如果Activity需要恢复时,提交之后的状态会丢失。如果丢是也没关系,使用commitAllowingStateLoss().
    

public abstract int commitAllowingStateLoss()

                      

Like commit() but allows the commit to be executed after an activity's state is saved. This is dangerous because the commit can be lost if the activity needs to later be restored from it state, so this should only be used for cases where it is okay for the UI state to change unexpectedly on the user.

类似于commit(),但是允许在Activity的状态被保存之后提交执行。这是危险的,因为Activity往后需要从它的状态恢复时,之前的提交可能已丢失,所以只有对于用户界面状态意外改变也无所谓的情况下,你才应该使用commitAllowingStateLoss()方法。

八、与Activity通信 虽然Fragment是独立于Activity的对象,并且可以在多个Activity中使用,一个给定的Fragment实例直接与包含它的Fragment相关联。 尤其是,Fragment可以通过getActivity()对象访问Activity实例,并且执行简单的任务,比如在Activity布局中查找一个视图。
View listView = getActivity().findViewById(R.id.list);
同样,通过从FragmentManager获取Fragment的索引,使用 findFragmentById() 或者 findFragmentByTag(), 你的Activity也可以调用Fragment的方法。
例如:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
九、创建Activity的回调事件
在默写情况下,你可能需要一个Fragment与Activity共享事件。最好的方式是在Fragment中定义一个回调接口,并在宿主Activity实现它。当activity通过interface接收到一个回调, 必要时它可以和在layout中的其他fragment分享信息。例如, 如果一个新的应用在activity中有2个fragment – 一个用来显示文章列表(framgent A), 另一个显示文章内容(fragment B) – 然后 framgent A必须告诉activity何时一个list item被选中,然后它可以告诉fragmentB去显示文章。
在这个例子中, OnArticleSelectedListener 接口在fragment A中声明:
public static class FragmentA extends ListFragment {    ...    // Container Activity must implement this interface    public interface OnArticleSelectedListener {        public void onArticleSelected(Uri articleUri);    }    ...}
然后fragment的宿主activity实现 OnArticleSelectedListener 接口,并覆写 onArticleSelected() 来通知fragment B,从fragment A传来的事件。为了确保宿主activity实现这个接口, fragment A的 onAttach() 回调方法(当添加fragment到activity时由系统调用) 通过将作为参数传入onAttach()的Activity做类型转换来实例化一个OnArticleSelectedListener实例。代码如下:
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);
//...
}

传给 onListItemClick() 的 id 参数是被点击的项的行ID,activity(或其他fragment)用来从应用的 ContentProvider 获取文章。

十、添加项目到ActionBar
你的fragment可以通过实现 onCreateOptionMenu() 提供菜单项给activity的选项菜单(以此类推, Action Bar也一样)。然而为了使这个方法接收调用,你必须在 onCreate() 期间调用 setHasOptionsMenu() 来指出fragment将会添加item到选项菜单(否则, fragment将接收不到对 onCreateOptionsMenu()的调用)。

随后从fragment添加到Option菜单的任何项,都会被追加到现有菜单项的后面。当一个菜单项被选择,fragment也会接收到 对 onOptionsItemSelected() 的回调。也可以在你的fragment layout中通过调用registerForContextMenu() 注册一个view来提供一个上下文菜单。当用户打开环境菜单,fragment接收到一个对 onCreateContextMenu() 的调用.当用户选择一个选项item时, fragment接收到一个对onContextItemSelected() 的调用。

注意: 尽管你的fragment会接收到它所添加的每一个菜单项被选择后的回调,但实际上当用户选择一个菜单项时,activity会首先接收到对应的回调。如果activity的on-item-selected回调函数实现并没有处理被选中的项目,然后事件才会被传递到fragment的回调。
这个规则适用于选项菜单和上下文菜单。

关于菜单更多的信息,查看 Menus and Action Bar 指导手册。


十一、处理Fragment的生命周期


图3. Fragment生命周期中宿主Activity生命周期的影响

管理Fragment的生命周期很像管理Activity的生命周期。就像Activity,Fragment可以存在三种状态:
1.Resumed:fragment在运行中的Activity是可视的。
2.Paused:另一个Activity处于前台并且取得焦点,但是这个Fragment的宿主Activity仍然可见(前台Activity是部分透明或者没有完全覆盖屏幕)。
3.Stopped:Fragment不可见。要么是宿主Activity已经停止stopped或者Fragment被移除但已添加到回退栈中。已停止stopped的Fragment仍然存活这(所有状态和成员信息被系统保持着)。然而,它对用户不再可见,并且如果宿主activity被干掉,其中的Fragment也会被干掉。

和activity一样, 你可以使用Bundle保持fragment的状态,万一activity的进程被干掉,并且当activity被重新创建的时候, 你需要恢复fragment的状态时就可以用到. 你可以在fragment的 onSaveInstanceState() 期间保存状态,并可以在 onCreate(),onCreateView() 或 onActivityCreated() 期间恢复它。

生命周期方面activity和fragment之间最重要的区别是各自如何在它的后台堆栈中储存。 在默认情况下,activity在停止后,它会被放到一个由系统管理的用于保存activity的后台堆栈。(因此用户可以使用BACK按键导航回退到它)。更多关于保存状态的信息请查看Activities文档。
Activity和Fragment最显著的不同是它们如何被存储在各自的回退栈中。当Activity停止时,Activity会默认被放置在由系统管理的Activities回退栈中(所以用户可以通过回退键导航回退到Activity,详情请看Tasks and Back Stack)。然而,仅当你在一个事务期间移除fragment时,显式调用addToBackStack()请求保存实例时,才被放到一个由宿主activity管理的后台堆栈。

然而,管理Fragment的生命周期非常类似于管理Activity的生命周期。所以, managing the activity lifecycle同样适用于Fragment。你需要理解的是,activity的生命如何影响fragment的生命。
注意:如果在你的Fragment中你需要一个Context对象,可以调用getActivity()。但是使用getActivity()要当心,只有当Fragment被绑定到Activity后(即onAttach())才能用。当Fragment没有绑定到Activity,或者Fragment生命周期末尾Fragment与Activity分离,getActivity()都会返回NULL。

十二、与Activity生命周期协调 Fragment宿主的Activity的生命周期直接影响Fragment的生命周期。 由此针对activity的每一个生命周期回调都会引发一个fragment类似的回调。例如,当activity接收到onPause()时,这个 activity之中的每个fragment都会接收到onPause()。

Fragment有一些额外的生命周期回调方法,然而,为了处理像是创建和销毁fragment界面,它与activity进行独特的交互。这些额外的回调方法是:

onAttach()
当fragment被绑定到activity时调用(Activity会被传入)。
onCreateView()
创建与fragment相关的视图体系时被调用。
onActivityCreated()
当activity的onCreate()函数返回时被调用。
onDestroyView()
当与fragment关联的视图体系正被移除时被调用。
onDetach()
当fragment正与activity解除关联时被调用。

fragment的生命周期流程实际上是受其宿主activity影响,如图3所示。在这 张图中,可以看到activity的每个连续状态是如何决定fragment可能接收到哪个回调函数的。例如,当activity接收到它的 onCreate()回调时,activity之中的fragment接收到的仅仅是onActivityCreated()回调。

一旦activity处于resumed状态,则可以在activity中自由的添加或者移除fragment。因此,只有当activity处于resumed状态时,fragment的生命周期才可以独立变化。

然而,当activity离开恢复状态时,fragment再一次被activity推入它的生命周期中。

范例


为了把这篇文档里的内容放到一起讨论,这里有一个使用两个fragment创建双区域布局的activity的范例。这个activity下面包含 的一个fragment用来显示莎士比亚戏剧列表,另一个fragment则用来在列表项被选中时显示该戏剧的概要。它也表明了如何基于不同屏幕配置提供 不同的fragment配置。

注意:这个activity的全部源代码可以在FragmentLayout.java获取。

在onCreate()期间,主activity用常用的方法应用了一个布局。

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

被应用的布局是fragment_layout.xml

 xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="horizontal"    android:layout_width="match_parent" android:layout_height="match_parent">     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" />     android:id="@+id/details" android:layout_weight="1"            android:layout_width="0px" android:layout_height="match_parent"            android:background="?android:attr/detailsElementBackground" />

使用这个布局,activity一载入布局,系统就实例化TitlesFragment(它列出了戏剧标题),尽管在屏幕右方的 FrameLayout(其中的fragment用来显示即将列出的戏剧概要)消耗了内存,但是起先是空的。就像你下面将要看到的,直到用户从列表中选择 一项时,fragment才被置于FrameLayout中。

然而,并不是所有的屏幕配置都足够宽,以显示并排的戏剧列表和摘要。所以,上面的布局仅适用于横向屏幕配置,且配置保存在res/layout-land/fragment_layout.xml。

因此,当屏幕被置于纵向时,系统会适应下面的布局,它被保存在res/layout/fragment_layout.xml:

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

这个布局只包含了TitlesFragment。这就意味着,当设备置于纵向时,只有戏剧标题列表是可见的。因此,当用户点击这个配置中的一个列表项时,应用程序将开启一个新的activity来显示摘要,而不是载入第二个fragment。

接下来,你可以看到这在fragment类里是如何完成的。首先是显示莎士比亚戏剧标题的TitlesFragment。这个fragment继承于ListFragment,并且靠它处理大部分的列表视图工作。

就像你看到的这段代码,注意当用户点击一个列表项时会产生两个可能的行为:这依赖于两个布局哪个处于激活状态,它要么创建并显示一个新的 fragment以在同一个activity中显示概要内容(添加fragment到FrameLayout),要么启动一个新的activity(在 fragment可显示的地方)。

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);        }    }}

第二个fragment,DetailsFragment在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;    }}


从TitlesFragment类回想一下,如果用户点击列表项,并且目前的区域不包含R.id.details视图(这是DetailsFragment的所在),那么应用程序启动DetailsActivity以显示列表项内容。

下面是DetailsActivity,它只是在屏幕纵向时,嵌入DetailFragment以显示被选中的戏剧概要。

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();        }    }}


请注意,如果配置是横向的,这个activity会结束掉自己,使主acitivity可以接管并在TitlesFragment旁显示 DetailsFragment。这种情况可能会发生在,用户在纵向时开启了DetailsActivity,但是之后旋转到横向(它重新启动了当前 activity)。

更多的关于使用fragment的例子(还有这个例子的完整源码),请参阅ApiDemos中的示例代码(从 Samples SDK component中下载可用的)。 



更多相关文章

  1. Android(安卓)ART invoke 代码生成
  2. Android---3---布局之LinearLayout
  3. Android(安卓)ActionBar 使用详解
  4. Android媒体相关开发应用程序接口
  5. Android(安卓)Studio 加载 .so库出现couldn't find "*.so"
  6. 使用线程执行堆栈StackTraceElement设计Android日志模块
  7. Android开发笔记(一百二十)两种侧滑布局
  8. 如何用eclipse编译调试adnroid的Browser
  9. android IPC通信中的UID和PID识别

随机推荐

  1. Android下多页显示技巧
  2. android用sharepreference保存输入框中的
  3. Android一键锁屏开发全过程【源码】【附
  4. android backgroud alpha
  5. 2010.12.26——— android 获得手机号码
  6. Android加速度传感器数值的过滤
  7. Android 之 setTextColor 写法
  8. android DisplayMetrics学习
  9. Android 中处理POWER/HOME流程
  10. 如何解决android NDK r8c 老是重新编译源