这一篇翻译的真是不容易啊.........
Fragments
一个fragment表示在一个activity中用户界面的一个行为或者一部分。你可以将几个fragment结合放在在一个activity中形成一个多版面UI(multi-pane UI),也可以在多个activity中重复使用一个fragment。你可以将fragment理解为一个activity的一个模块化部分,fragment在activity运行期间拥有自己的生命周期(某种可以在不同activity中重复使用的activity子类)。
一个fragment必须嵌套在一个activity中,并且fragment的生命周期会被持有它的activity的生命周期直接影响。例如,当activity暂停的时候,则它内部的所有fragment也全部暂停,同样的如果activity被摧毁,则内部的所有fragment也被摧毁。然而,当一个activity正在运行时(生命周期中的resumed状态),你可以独立的操作每一个fragment,像是添加或者删除它们。当你执行这样一个片段事务的时候(when youperform such a fragment transaction),你也可以将它添加到一个backstack(被activity管理)——每一个back stack进入activity都产生一个fragment事务的记录。点击后退键,Backstack允许用户返回一个fragment事务。(The back stack allows the user to reverse afragment transaction(navigate backwards), by pressing the Back button)
当你给你的activity布局添加一个fragment让其作为activity中的一部分时,这个fragment存在于一个ViewGroup中,而这个ViewGroup又包含在activity的view层级里,(itlives in a ViewGroup inside the activity’s view hierarchy)这个fragment定义有它自己的view布局。在activity的layout文件中用<fragment>元素声明fragment可以向一个activity中插入fragment,或者也可以用代码向一个已经存在的ViewGroup中添加。然而,一个fragment并不是activity必要的一部分,你可以定义一个没有UI的fragment,使它作为一个不可见的工作者为activity服务。
这篇文档描述了你的app如何使用fragment,包括当fragment添加到activity的backstack中它们如何保持其状态,与activity或者activity中的其他fragment分享事件(share events),对activity的action bar做出贡献等。
Design Philosophy
Android在Android3.0 (API Level 11)中引入了fragment,主要是为了在大屏幕(平板)设备上提供更动态更灵活的UI设计。因为平板屏幕比移动设备要大很多,所以有更多的空间结合和交换UI组件。Fragment让这种设计不需要你来管理视图层次变化。将一个activity的布局划分成几个fragment,你可以在activity运行期间修改它的外观,并且在由这个activity控制的back stack中将这些改变保存下来。
如下图所示,一个新的app可以使用一个fragment在屏幕的左边展示文章列表,在屏幕的右边显示文章的内容——两个fragment在同一个activity中显示,每个fragment都有自己的生命周期设置方法,各自处理自己的输入事件。和在一个activity中显示文章列表,在另一个activity中显示文章内容这种设计相比,在同一个activity中显示文章列表和文章内容更方便用户使用。
你应该将每一个fragment都设计为模板的、可重复使用的activity组件。因为每个fragment都声明了它自己的布局,根据它自己的生命周期回调方法具有它自己的行为,所以你可以把一个fragment包含在多个activity中,同时应该避免直接从一个fragment对另一个fragment进行操作。这非常重要,因为一个模块化的fragment允许你根据屏幕尺寸改变你的fragment组合。当设计一个app同时应用在平板和移动设备上时,你可以根据可使用的屏幕空间调用不同的layout配置,重复使用你的fragment。
上图就说明了两个fragment是如何在平板上被结合在一个activity中,又如何在移动设备上被分开。
更多关于在不同设备配置中不同的fragment组合情况,参考文档Supporting Tablets and Handsets.
Creating a Fragment
为了创建一个fragment,你必须要创建一个Fragment的子类(或者一个已经存在的Fragment的子类)。Fragment类的代码与Activity的很相似。它包含的回调方法与activity的很类似,就像是onCreate(), onStart(), onPause()和onStop()。(插播:其实就是简单的复制粘贴改改即可)
通常情况下,你至少应该实现下面的生命周期方法:
onCreate ()
当创建fragment的时候系统会调用这个方法。在这个方法中你应该初始化当fragment暂停,停止或者运行时需要维持的主要元件。
onCreateView ()
当首次给fragment的用户界面绘制时,系统会调用这个方法。为了给你的fragment 绘制UI,你必须要用这个方法返回一个View。这个view就是你的fragment的layout的root。
onPause ()
当用户离开这个fragment的时候(尽管这不总意味着这个fragment将被销毁),系统会调用这个方法作为first indication。在这个方法中,你应该提交在当前用户会话中应该保存的改变。(因为用户可能不会回来了)
大多数app至少应该对每个fragment执行这三个方法,但是其他的几个回调方法你也应该用来处理fragment生命周期中的各种阶段。更多信息参见Handing theFragment Lifecycle.
下面是你可能会用到的Fragment的子类:
DialogFragment
显示一个流动的对话框。和使用Activity类的对话框helper method比起来,使用这个类创建一个对话框是一个可选择的好方式,becauseyou can incorporate a fragment dialog into the back stack of fragments managedby the activity, allowing the user to return to a dismissed fragment.
ListFragment
显示了一个由adapter(类似一个SimpleCursorAdapter)控制的列表项,类似ListActivity。它提供了几个方法来控制一个list view,类似onListItemClick()回调处理点击事件。
PreferenceFragment
将Preference对象的层次像列表一般显示,类似于PreferenceActivity。当你给你的app创建“设置/settings”activity的时候会非常有用。
Adding a user interface
Fragment通常用作activity用户界面的一部分,控制着它自己的layout。
为fragment提供layout,你必须实现onCreateView()回调方法,系统会在fragment应该绘出它的layout的时候调用这个方法。你必须调用的这个方法返回的View就是你的fragment的layout的root。
Note:如果你的fragment是ListFragment的子类,默认从onCreateView()方法返回的是ListView,所以你不需要执行这个方法。
为了从onCreateView()返回一个layout,你可以从XML中定义的layout resource中填充它。为了帮助你实现上述的方法,onCreateView()提供了一个LayoutInflater对象。
例如,下面是Fragment的子类,从example_fragment.xml文件中下载layout:
publicstatic class ExampleFragment extends Fragment{
@Override
public View onCreateView (LayoutInflaterinflater, ViewGroup container,
Bundle savedInstanceState) {
//Inflate the layout for thisfragment
return inflater.inflate (R.layout.example_fragment,container, false);
}
}
container参数传递给onCreateView()是父类ViewGroup(来自activity的layout中),你的fragment要插入的就是这个ViewGroup。savedInstanceState参数是一个Bundle,如果这个fragment将运行(fragment状态恢复会在Handling the Fragment Lifecycle中讨论),它会为提供fragment之前的状态的数据。
<Creating a layout R.layout.example_fragment是一个layout资源的引用,该资源就叫做example_fragment.xml,保存在app资源中。更多关于在XML中创建layout的信息,参见UserInterface>
inflate()方法有下面三个内容提要:
● 你要inflate(填充)的layout的资源ID
● ViewGroup是填充的layout的父类。将container传递进来非常重要,这样系统可以将layout参数应用到填充的layout的rootview,特别是当这个父类View正在运行的时候
● 一个boolean指出这个填充的layout在填充期间是否应该附在父类ViewGroup(第二个参数)上。(在这个情况下,false是因为系统已经将inflate layout插入container中了——如果传递的是true,则会在最终layout上创建一个多余的view group)
现在你已经知道如何创建一个提供layout的fragment了。下一步,你需要将这个fragment添加到activity上。
Adding a fragment toan activity
通常情况下,一个fragment作为持有它的activity UI的一部分,即作为activity的全部view hierarchy的一部分。有两种方法你可以将fragment添加到activity的layout中:
● 在activity的layout文件中声明fragment
这种情况下,你可以为fragment指定layout的内容,将fragment当成一个view。例如,下面的layout文件就是一个activity中包含两个fragment:
<?xml version=”1.0” encoding=”utf-8”?>
<LinearLayoutxmlns: 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>
在<fragment>中的android:name属性指定Fragment类在这个layout中实例化。当系统创造了这个activity layout的时候,它会为每个fragment实例化,同时会为每个fragment调用onCreateView()方法来检索每一个fragment的layout。系统会在<fragment>元素的位置直接插入View。
Note:每一个fragment需要独特的ID让可以让系统在activity重启的时候(同时你也可以使用这个ID捕获fragment来执行事务,例如移动它)恢复这个fragment。有三种方式可以为一个fragment提供ID:
○ 使用android:id 标签提供一个唯一的ID
○ 使用android: tag标签提供一个唯一的字符串
○ 如果你不选择上面给两个方法,系统使用容器view的ID(containerview)
● 通过代码将fragment添加到已经存在的ViewGroup
在你的activity运行的任何时候你都可以将fragment添加到activity的layout中。你只需要简单的指定存放fragment的ViewGroup。
为了在你的activity中执行fragment事务(例如添加,移动,替换一个fragment),你必须使用FragmentTransactionAPI。从activity中获得FragmentTransaction的实例如下:
FragmentManagerfragmentManager=getFragmentManager ();
FragmentTransactionft=fragmentManager.beginTransaction ();
然后你可以使用add()方法添加fragment,指定要插入的fragment和被插入的view:
ExampleFragmentfragment=new ExampleFragment ();
ft.add(R.id.fragment_container, fragment);
ft.commit();
add()方法中的第一个参数就是fragment应该放置的地方,用资源ID来指定,第二个参数就是放置在这个地方的fragment实例。
一旦你使用FragmentTransaction对fragment做了改变,你应该调用commit()方法使这些改变生效。
Adding a fragment without UI
上面的例子展示了如何在你的activity中添加提供UI的fragment。然而,你也可以使用fragment为activity提供一个后台行为,不需要呈现UI。
使用add(Fragment, String)可以向activity添加无UI的fragment(要为fragment提供一个唯一的”tag”string,而不是给fragment提供view ID)。因为它在activitylayout中并不与任何一个view关联,所以这个fragment不会获得onCreateView()的调用,所以你也不用实现这个方法。
为fragment提供tag的string并不只是为非UI fragment服务的——你也可以为有UI的fragment提供tag——但是如果一个fragment没有UI,这个字符串tag是唯一能够识别它的标志。如果你希望从activity中获取这个fragment,你需要使用findFragmentByTag()。
Managing Fragments
为了管理你的activity中的fragment,你需要使用FragmentManager。你可以在activity中使用getFragmentManager()获得这个类的对象。
你可以使用FragmentManager做下面事情:
● 使用findFragmentById ()获得activity中已经存在的fragment(针对在activity的layout中提供UI的fragment),或者使用findFragmentByTag()获得提供/不提供UI的fragment
● 使用popBackStack()将fragment放入back stack(模拟由用户操作返回键)
● addOnBackStackChangedListener()可以注册一个listener监听backstack的改变
更多信息可以查看FragmentManager文档。
像之前演示的那样,你也可以使用FragmentManager开启一个FragmentTransaction,这个类允许你提交事务,例如添加或删除fragment。
Performing FragmentTransactions
在activity中使用fragment的特征是对用户交互产生添加,删除,替换fragment和表现其他的action的响应。每个你提交给activity的改变都可以称为“transaction”,你可以使用FragmentTransaction来执行。你也可以将每一个transaction保存在由activity管理的backstack中,允许用户通过fragment的改变向后导航。(与activity的向后导航类似)
你可以如下通过FragmentManager来获取一个FragmentTransaction实例:
FragmentManagerfragmentManager = getFragmentManager ();
FragmentTransactionfTransaction = fragmentManager.beginTransaction ();
每个transaction是你想在同一时间内执行的一系列改变。你可以使用add(),remove(),replace()等方法建立一个transaction,里面有你想执行的一系列改变。然后,必须使用commit()将这个transaction应用到activity中。
在你调用commit()之前,你可以想要调用addToBackStack()方法将transaction添加到back stack中。这个backstack会被activity管理,同时允许用户点击back按钮返回fragment之前的状态。
例如,下面是你如何用一个fragment替换另一个,以及在back stack中保存之前的状态:
//Createnew fragment and transaction
FragmentnewFragment= new ExampleFragment ();
FragmentTransactiontransaction=getFragmentManager ().beginTransaction ();
//Replacewhatever is in the fragment_container view with this fragment, and
//add the transaction to the back stack
transactin.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
//Committhe transaction
transaction.commit();
在这个例子中,newFragment替换了通过资源ID识别的layout容器中存在的当前fragment。调用addToBackStack()方法,替换的transaction会被保存在back stack中,用户可以通过点击返回键退回(reverse)这个transaction并且带回之前的fragment。
如果你在这个transaction中添加了多个操作(例如还有add()/remove())同时调用了addToBackStack(), 那么所有的改变都会作为一个单独的transaction被添加到back stack并且点击返回键也会将它们全部退回。
你给一个FragmentTransaction添加各种fragment的改变没什么规则,除了:
● 你必须在最后才调用commit()
● 如果你在同一个容器中添加多个fragment,你添加它们的顺序决定了它们在view hierarch中的显示。
如果你在执行一个transaction去替换一个fragment的时候并没有调用addToBackStack(),那么这个fragment会在提交的时候被摧毁且用户也不能通过点击返回键返回到原来fragment。相反,如果调用了addToBackStack(),则被替换的fragment会停止同时可以通过返回键恢复。
Tip:在提交之前,对每个fragment Transaction你可以调用setTransaction()应用一个transaction动画
调用commit()并不马上执行transaction,取而代之,当activity的UI线程一可以运行,它就安排这个transaction在activity的UI线程运行。如果必要的话,你可以在UI线程中调用executePendingTransactions()方法立即执行transaction的提交。一般情况下,没必要这样做,除非thetransaction is a dependency for jobs in other thread.
Caution:你只能在activity保存它的状态(当用户离开这个activity)之前使用commit()提交一个transaction。如果你在这个点之后提交,就会抛出错误。这是因为在提交之后这个状态可能会丢失。(插播:则后退回到这个activity的时候则无法获得fragment的信息)如果你允许丢失的情况出现,可以使用commitAllowingStateLoss()方法。
Communicating with the Activity
特别的,fragment可以用getActivity()使用Activity示例来执行任务,例如找到activitylayout中的一个view:
Viewlistview=getActivity ().findViewById (R.id.list);
同样的,使用findFragmentById()或者findFragmentByTag()方法,你的activity也通过从FragmentManager获得的Fragment引用来使用fragment里面的方法。例如:
ExampleFragmentfragment = (ExampleFragment) getFragmentManager ().
findFragmentById (R.id.example_fragment);
Creating eventcallbacks to the activity
在一些情况下,你可能需要一个fragment和activity共享events。实现它的一个比较好的方法是在fragment中声明一个callback接口,然后让拥有这个fragment的activity执行这个接口。当activity通过接口获得一个callback,它会在必要情况时和在layout中的其他fragment分享信息。
例如,如果一个新闻app在一个activity中有两个fragment——一个显示文章列表(fragment A)而另一个显示文章(fragmentB)——那么fragment A必须告诉activity列表中的一个item被选中,然后activity告诉fragmentB显示这篇文章。在这种情况下,在fragment A中声明OnArticleSelectedListener。
publicstatic class FragmentA extends ListFragment {
…
//Container Activity must implement thisinterface
public interfaceOnArticleSelectedListener {
public void onArticleSelected (UriarticleUri);
}
}
然后持有Fragment A的activity实现上面这个接口,重写里面的方法告知FragmentB在Fragment A中发生的events。为了确定activity实现了上述接口,fragment A的onAttach()回调方法(系统会在fragment添加到activity的时候调用)通过传递来的Activity实例化OnArticleSelectedListener实例:
publicstatic 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”);
}
…
}
}
如果activity没有实现这个接口,那么fragment会抛出ClassCastException。如果成功的话,mListener持有activity实现的OnArticleSelectedListener的引用,通过实现接口的方法,fragment A和activity可以分享events。如下,Fragment A是ListFragment的继承者,当用户每次点击列表中的item时,系统会调用onListItemClick()方法,在其中调用onArticleSelected()就可以与activity共享event了:
publicstatic class FragmentA extends ListFragment {
OnArticleSelectedListener mListener;
…
@Override
public void onListItemClick (ListView l,View v, int position, long id) {
//Append the clicked item’s row IDwith the content provider Uri
Uri uri =ContentUris.withAppendedId (ArticleColumns.CONTENT_URI, id);
//Send the event and Uri to thehost activity
mListener.onArticleSelected (uri);
}
}
方法里面的参数id就是点击item的rowID,activity可以使用它从app的ContentProvider中获取文章。更多信息参见ContentProvider。
Adding items to the Action Bar
通过实现onCreateOptionsMenu()你的fragment可以给activity的Option Menu提供菜单。(同理给ActionBar提供也可以)为了让这个方法获得调用,你必须在onCreate()期间调用setHasOptionsMenu()声明fragment可能要给OptionMenu添加item。(否则,fragment不会收到onCreateOptionsMenu())
任何从fragment中添加到Option Menu的item是附加到已经存在的菜单item上的。当菜单item被选中的时候,fragment也会收到回调方法onOptionsItemSeleted()。
通过方法registerForContextMenu(),你也可以在你的fragmentlayout中加入一个View来提供快捷菜单(context menu)。当用户打开这个快捷菜单的时候,fragment会收到方法onCreateContextMenu(),而当用户选择一个item的时候,fragment会收到方法onContextItemSelected()。
你也可以在fragment的layout里面注册一个view,调用registerForContextMenu()方法使得view可以提供快捷菜单。当用户打开这个快捷菜单的时候,fragment会收到方法onCreateContextMenu()的调用。当用户选择一个item的时候,fragment会收到方法onContextItemSelected()的调用。
Note:尽管每次点击item的时候,你的fragment都会收到点击item的回调方法,activity会首先收到点击menu的回调方法。如果activity对你选择的item没有实施相应的处理方法,那么这个事件会传递给fragment的回调方法。This is true for the Options Menu and Contextmenus.更多信息参见Menus和Action Bar。
Handling theFragment Lifecycle
管理fragment的生命周期和管理activity的生命周期非常相似。和activity一样,一个fragment可以在三个状态下存在:
Resumed
在运行状态下的activity中,这个fragment可见
Paused
另一个activity在前台且获得焦点,但是拥有该fragment的这个activity依然可见(前端activity透明或者占据了屏幕的一部分位置)
Stopped
这个fragment不可见。或者是持有它的activity已经停止,或者是fragment已经从该activity中移除并添加到backstack中。一个停止的fragment依然是alive的(其所有的状态和成员信息仍然被系统保留)然而,它对用户不再可见且当activity被杀掉后,它也会被杀掉。
<右图:activity生命周期对fragment生命周期的影响,可以看到对应的activity的生命周期中,fragment可以接收到的回调方法。例如,在activity接收到onCreate()期间,fragment不会接收到超过onActivityCreated()以外的方法。>
和activity一样,你可以使用Bundle保存fragment的状态,这样在activity线程被关闭而在activity被重新创建的时候,你可以再次使用这个fragment的状态。你可以在fragment的onSaveInstanceState()方法期间保存fragment的状态,在onCreate(),onCreateView(),onActivityCreated()方法中恢复fragment的状态。
在activity和fragment生命周期中最不相同的是它们怎么存储在相关的backstack中。当activity停止的时候,默认情况下它会被存储在由系统管理的back stack中(所以用户可以使用返回键找到它,我们会在Tasksand Back Stack中讨论)而fragment则会放置在由持有它的activity管理的backstack中,同时只有你在移除fragment的事务中显式调用方法addToBackStack(),fragment才可以存储在backstack中。
Cautions:如果你需要在你的Fragment中使用Context对象,可以调用getActivity()。注意只有当fragment附着在activity上才可以调用这个方法,如果fragment没有附着在activity上,或者fragment在其生命周期结束时和activity分离,调用这个方法会返回null。
Coordinating withthe activity lifecycle
Fragment依附的activity的生命周期对该fragment的生命周期有直接的影响,就像activity生命周期的回调方法会影响相应的fragment的生命周期的回调方法。例如,activity收到onPause()方法,则在activity中的fragment也会收到onPause()方法。
Fragment有几个特殊的生命周期回调方法,处理与activity的特殊交互来执行操作,例如创建和销毁fragment的UI。这些额外的回调方法是:
onAttach ()
当fragment已经和activity关联的时候调用(activity在这里传递)
onCreateView ()
调用这个方法创建与fragment关联的view层级结构(viewhierarchy)
onActivityCreated ()
当activity的onCreate()方法返回的时候调用
onDestroyView ()
当与fragment关联的view hierarchy(view层级结构)被移除的时候调用
onDetach ()
当fragment正在与activity取消关联的时候调用
当activity到达resumed状态,你可以在activity中自由的添加或移动fragment,并且只有activity在resumed状态下fragment的生命周期可以直接改变。
当activity离开resumed状态的时候,fragment会被activity再次推动经历一次生命周期。(However,when the activity leaves the resumed state, the fragment again is pushedthrough its lifecycle by the activity.)
Example
下面这个例子是在一个activity中使用两个fragment创建一个有两个窗口的布局。其中一个fragment显示Shakespeare戏剧的标题,另一个fragment显示选择的戏剧的摘要。这个例子也展示了如何根据屏幕的结构来提供不同的fragment结构。
在主要activity的onCreate()期间应用layout:
@Override
protectedvoid onCreate (Bundle savedInstanceState) {
super.onCreate (savedInstanceState);
setContentView (R.layout.fragment_layout);
}
应用在fragment_layout.xml中的layout如下:
<LinearLayoutxmlns: 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>
使用这个layout,系统会在activity加载layout的时候初始化TitlesFragment(列出戏剧标题),而FrameLayout(显示戏剧的摘要的fragment)会占据屏幕的右边,但是会在启动初期显示空白。只有当用户选择列表中的item时,显示摘要的fragment才会被放入FrameLayout中。
然而,并不是所有的屏幕都足够大能够同时在屏幕显示列表和内容,所以上面的layout只用在宽屏的设备上,且把它存储在res/layout-land/fragment_layout.xml。
如果屏幕是竖屏,那么系统会应用下面储存在res/layout/fragment_layout.xml中的:
<FrameLayoutxmlns: android=http://schemas.android.com/apk/res/android
android: layout_width=”match_parent”
android: layout_height=”match_parent”>
<fragment
class=”com.example.android.apis.FragmentLayout$TitlesFragment”
android:id=”@+id/titles”
android: layout_width=”match_parent”
android: layout_height=”match_parent”/>
</FrameLayout>
这个layout只包含TitlesFragment。这意味着当设备在竖屏的状态下,只显示戏剧的标题。当用户点击列表的item时,app会启动一个新的activity显示戏剧的摘要而非载入第二个fragment。
下面你可以看见完整的实现fragment布局的类。第一个是TitlesFragment,这个fragment继承ListFragment同时依赖它处理大部分listview的工作。
注意当用户点击列表的item的时候,有两种可能的行为,这两种行为取决于这横屏和竖屏的layout之中哪一个是active的。如果设备处于横屏的情况,那么点击列表的item可以在同一个activity中创建并显示一个新的展示戏剧摘要的fragment(这个fragment添加到FrameLayout中),如果设备处于竖屏的情况,则点击列表的item会启动一个新的activity。(戏剧摘要的fragment会在新的activity中显示)
publicstatic class TitlesFragment extends ListFragment {
boolean mDualPane;
int mCurCheckPosition = 0;
@Override
public void onActivityCreated (BundlesavedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Populate list with our staticarray of titles
setListAdapter (newArrayAdapter<String>(getActivity(),
android.R.layout_simple_list_item_activated_1,Shakespear.TITLES));
//Check to see if we have a framein which to embed the details
//fragment directly in thecontaining UI
View detailsFrame= getActivity().findViewById (R.id.details);
mDualPane =
detailsFrame! =null && detailsFrame.getVisibility() ==View. VISIBLE;
if (savedInstanceState! =null) {
//Restore last state forchecked position
mCurCheckPosition =savedInstanceState.getInt (“curChoice”, 0);
}
if (mDualPane) {
//Indual-pane mode, the list view highlights the selected item
getListView().getChoiceMode (ListView.CHOICE_MODE_SINGLE);
//Makesure our UI is in the correct state
showDetails(mCurCheckPosition);
}
}
//1、当用户按下HOME键时是显而易见的,系统不知道你按下HOME后要运行多//少其他的程序,自然也不知道activity A是否会被销毁,故系统会调用//onSaveInstanceState,让用户有机会保存某些非永久性的数据。以下几种情//况的分析都遵循该原则2、长按HOME键,选择运行其他的程序时。3、按下电//源按键(关闭屏幕显示)时。4、从activity A中启动一个新的activity时。//5、屏幕方向切换时,例如从竖屏切换到横屏时。在屏幕切换之前,系统会销//毁activity A,在屏幕切换之后系统又会自动地创建activity A,所以//onSaveInstanceState一定会被执行
@Override
public void onSaveInstanceState (BundleoutState) {
super.onSaveInstanceState(outState);
outState. putInt (“curChoice”,mCurCheckPosition);
}
@Override
public void onListItemClick () {
showDetails (position);
}
//Helperfunction 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 inwhich it is //displayed.
private void showDetails (int index) {
mCurCheckPosition = index;
if (mDualPane) {
//We can display everything in-place with fragments, so update the //listto 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 fragmentto show this selection
details=DetailsFragment.newInstance(index);
//Execute atransaction, replacing any existing fragment
//with this oneinside the frame
FragmentTransactionft=getFragmentManager().
beginTransaction();
if (index==0) {
ft.replace(R.id.details,details);
} else {
ft.replace(R.id.a_item,details);//whatthe hell??CHECK!
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
ft.commit();
}
} else {
//Otherwisewe need to launch a new activity to display the fm
Intentintent=new Intent();
intent.setClass(getActivity(),DetailsActivity.class);
startActivity(intent);
}
}
}
第二个fragment叫做DetailsFragment,显示从TitlesFragment中选择item的内容:
publicstatic class DetailsFragment extends Fragment {
//Create a new instance of DetailsFragment, initialized to show the textat //‘index’
public static DetailsFragment newInstance(int index) {
DetailsFragment f=newDetailsFragment ();
Bundle args=new Bundle();
args.putInt(“index”, index);
f.setArguments(args);
return f;
}
public int getShownIndex () {
return gerArguments().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’scontaining //frame doesn’t exist. The fragment may still be created from itssave state, //but there is no reason to try to create its view hierarchybecause it won’t be //displayed. Note this is not needed—we could just run thecode below, //where we would create and return the view hierarchy; it wouldjust never be //used.
return null;
}
ScrollView scroller = newScrollView (getActivity());
TextView text = newTextView(getActivity());
int padding= (int)TypedValue.applyDimension
(TypedValue.COMPLEX_UNIT_DPI,4,
getActivity().getResource().getDisplayMetrics());
text.setPadding (padding, padding,padding, padding);
scroller.addView ();
text.setText (Shakespear.DIALOGE[getShownIndex()]);
return scroller;
}
}
从TitlesFragment类的调用可知,如果用户点击列表item且此时当前layout不包含R.id.detailsView(DetailsFragment属于这个View),那么app会开启DetailsActivity展示这个item的内容。(即竖屏的时候)
下面就是DetailsActivity内容,里面只有DetailsFragment来展示选择item的内容:
publicstatic class DetailsActivity extends Activity {
@Override
protected void onCreate (Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getResource().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 ) {
//Duringinitial setup, plug in the details fragment
DetailsFragmentdetails=new DetailsFragment();
details.setArguments(getIntent().getExtras());
getFragmentManager().beginTransaction().
add(android.R.id.content,details).commit();
}
}
}
注意这个activity会在横屏的时候结束自己,所以main activity会接收并在TitlesFragment旁边展示DetailsFragment。当用户在竖屏时开启DetailsActivity,又旋转屏幕成为横屏的时候(重新启动当前activity),这种情况就会出现。
更多使用fragment的例子,请参见API Demos sample in ApiDemos。