2.2.1 Fragments

这一篇翻译的真是不容易啊.........


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.更多信息参见MenusAction 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

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值