Fragment详解
原文链接:http://developer.android.com/guide/topics/fundamentals/fragments.html
一个fragemnt在一个活动中代表一个行为或用户界面的一部分。你可以在一个单一的Activity中组合使用多个fragment以建立一个多窗格的UI,并且可以在多个活动中重用一个fragment。你可以认为是一个拥有独立生命周期、能够独立接受输入事件、并且可以在活动运行时添加或移除的碎片作为一个活动的模块化部分(有点像一个你可以在不同活动中重用的子activity)。
一个fragment必须总是嵌入到一个activity中,并且它的生命周期直接受到主activity的生命周期的影响。例如:当活动暂停或销毁时,它里边的所有fragment也是如此。然而,当一个activity运行时,它在onResume()生命周期,你可以单独的操作每个碎片,例如添加或移除它们。当你执行了这样的操作,你也可以将它添加到fragment回退栈中——回退栈允许用户通过按返回按钮退回上一个fragment事务(向后导航)。
当你增加一个Fragment来作为Activity界面布局的一部分时,它存在在Activity视图层里面的一个ViewGroup中,你可以通过在Activity的布局文件中用声明这个fragment来把这个fragment插入你的 activity中,或者你可以在应用的代码中把他添加添加到一个现存的ViewGroup中。然而,Fragment不必是一个activity布局的一部分,你可以使用一个Fragment而不使用他的UI,这样可以让Fragment作为Activity的一个不可见的部分来工作.
Design Philosophy-设计理念
Android在Android3.0(API等级11)中引入了Fragment,主要是为了在大屏幕上(比如平板)支持更多动态的灵活的UI设计。因为平板的屏幕比其他手持设备大多了,有更多的空间来组合,交换UI组件。Fragment使你在View层不必进行很复杂变化就可以就可以实现这些设计。通过把Activity的布局分解成很多Fragment,你可以在运行时改动activity的界面并且可以把这些变化保存在activity管理的返回栈中.
比如,一个新闻应用可以使用一个Fragment在左边显示一列文章标题而在左边的另一个Fragment显示文章详细内容.这两个 Fragment都在同一个Activity中,他们并排着,每个Fragment有他自己的生命周期回调方法,处理他们各自的输入事件。那么,不需要在一个activity中选择在另一个activity中阅读,用户可以选择一篇文章在同一个activity中阅读这个新闻的内容。如图所示:
你应该把fragment设计成模块化的,可复用的Activity组件。就是说,每个Fragment定义了他自己的布局和他自己的,拥有自己生命周期回调的行为,你可以在多个activity中包含一个Fragment,所以你应该把Fragment设计成可以复用的,并且需要避免从一个fragment直接操纵另一个fragment,这一点是非常重要的,因为一个fragment模块允许你针对不同的屏幕尺寸变化你的fragment组合形式。在设计应用来支持平板和手持设备时,你可以在不同的布局配置中重用你的fragment来针对屏幕空间优化用户体验。比如,在一个手持设备上,可能需要分开的fragment来提供一个单独窗口UI而不是使很多fragment在同一个activity中放不开.
Creating a Fragment-创建一个Fragment
创建一个Fragment,你应该创建一个Fragment的子类(或者他的一个现有子类).一般的,你至少应该实现下面的生命周期方法:
-
onCreate()
创建fragment的时候,系统会调用这个方法.在你实现过程中,当fragment暂停(pause),停止(stop)然后恢复(resume)时。
- onCreateView()
在fragment第一次绘制他的用户界面的时候系统会调用这个方法.如果你想为你的fragment绘制界面,你必须从这个方法中返回一个View,这个View是你fragment布局的基础.如果这个Fragment不提供UI,你可以返回空.
-
onPause()
系统调用这个方法作为用户离开这个fragment的第一标志(虽然这不总是意味着这个Fragment被摧毁了).通常是你需要做一些改变,这些改变超出了当前的用户会话(因为用户有可能不会回到这个界面来).
大多数应用应该至少为每个fragment实现上述的三个方法,但还有些回调函数你需要去实现,用来处理fragment生命周期中的不同状态。也有有些子类(不是基本的Fragment类)你可能想要继承来实现Fragment:
-
DialogFragment
- 显示一个浮动的对话框.使用这个类来创建一个对话框是和使用对话Helper方法在Activity类中创建对话框都是很好的方法,因为你可以把Fragment对话框包含在activity管理的Fragment返回栈中,允许用户返回到关闭的Fragment中.
-
ListFragment
- 展示一列被adapter(比如SimpleCursorAdapter)管理的项,和ListActivity很相似.它提供了一些管理一个列表视图的方法,比如处理点击事件的onListItemClick()方法.
-
PreferenceFragment
- 用一个列表来显示一组偏好设置对象,类似于PreferenceActivity. 在创建设置型的activity时会用到.
Adding a user interface-添加一个用户接口
为了给fragment提供一个布局,你必须实现onCreateView()方法,Android系统在Fragment绘制他的界面的时候调用这个方法.你对这个方法的实现必须返回一个View,这个View是你Fragment布局的基础.
注意:如果你的Fragment是一个ListFragment类的子类,默认会从onCreateView()返回一个Listview,所以你不需要实现它.
为了从onCreateView()方法返回一个布局,你可以用一个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); } }
Creating a layout-创建一个布局
传递给onCreateView()的容器参数是fragment锁插入的activity的父ViewGroup(来自对应的activity布局).savedInstanceState的参数是一个提供关于之前Fragment状态数据的Bundle,如果这个Fragment被恢复了 。
inflate()方法接收三个参数:
- 你想要添加的layout的资源ID.
- 将作为填充布局的父容器的ViewGroup.传递容器参数是非常重要的,只用这样才能使系统应用布局参数到填充视图的根视图,从而被它的父视图所确定。
- 一个boolean类型的参数,用于在填充时指明填充的布局是否应该附加在ViewGroup(第二个参数)上.(如果系统已经插入这个填充布局到容器了就返回false,如果将要在最终布局中创建一个多余的viewgroup,那就返回true)
Adding a fragment to an activity-给一个Activity添加一个Fragment
一般的一个fragment提供了Activity UI的一部分,他作为Activity全局视图层的一部分而嵌入.有两种方法可以把fragment嵌入到Activity布局中:
- 在Activity布局文件中声明Fragment.*
这样的话,你可以把Fragment当作一个视图,比如,这是一个嵌入两个Fragmet的Activity:
<?xml version"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>
注意.每个fragment需要一个独一无二的id以便系统可以在activity restart时恢复fragment。有三种方法为一个fragment提供id:
-
使用android:id来指定它唯一的ID.
-
使用android:tag来指定一个唯一的字符串标志
-
如果上面两个你都不指定,系统会使用容器视图的id,或者,机械性的把fragment添加到ViewGroup中.
在任何Activity运行的时候,你都可以把fragment添加到Activity的视图中.你只需要指定一个用于盛放 Fragment的ViewGroup.为了让fragment可以被管理(比如添加,删除,替换fragment),你必须使用来自 FragmentTransaction的API.你可以像下面这样在Activity中获取一个FragmentTransaction的实例:
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
你可以使用Add()方法添加一个Fragment,指定要添加的Fragment和目标View,如下:
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container, fragment); fragmentTransaction.commit();
add()方法中的第一个参数是Fragment所要放置的目标ViewGroup,通过资源ID指定,第二个参数是要添加的Fragment.只要你使用FragmentTransaction做了修改,你必须调用commit()方法来使修改生效.
Adding a fragment without a UI-添加一个没有UI的Fragment
上面的例子想你展示了怎么添加一个含有UI的Fragment到你的Actvity.然而,对于不想增加而外UI的Activity来说,你也可以使用Fragment来进行后台行为.
为了添加一个没有UI的Fragment.需要使用add(Fragment, String) 方法,其中,你需要为Fragment提供一个字符串的标志而不是一个视图ID.这样增加的Fragment,由于没有涉及到Activity的视图,所以不会调用onCreateView()方法.所以你不需要实现这个方法.
为Fragment提供一个字符串标志不一定只局限于没有UI的Fragment,你也可以为有UI的Fragment指定一个字符串标志,但是如果这个Fragment真的没有UI,那这个字符串标志是确定它的唯一标志.如果你想在后面从Activity中获取到这个fragment,你需要使用 findFragmentByTag()方法.
Managing Fragments-管理fragment
为了管理你Activity中的fragment,你需要使用FragmentManager.你可以通过你Activity中的getFragmentManager()来获取它.
使用FragmentManager你可以:
-
使用findFragmentById()(提供UI的Fragment)或者findFragmentByTag()(没有提供UI的Fragment) 获取你Activity存在的Fragment,
-
使用popBackStack()把Fragment从返回栈中弹出(模拟用户的返回命令).
-
使用addOnBackStackChangedListener()方法为返回栈的变化注册监听器.
Performing Fragment Transactions-执行Fragment事务
每个事务是一系列你想要同时执行的Fragmen的变化.你可以使用像add(),remove(),replace()这样的方法来为一个事务设定你想要执行的操作.为了使Activity的事务生效,你必须执行commit()方法.
在你调用commit()方法的之前,为了添加这个事务到一个Fragmen事务的返回栈,你可能想要调用addToBackStack()方法.这个返回栈被Activity管理,允许用户通过按下返回按键返回之前的Fragmen状态.
这里展示了怎么使用一个Fragmen替换另一个,然后在返回栈中返回到之前的状态.
// Create new fragment and transaction //创建一个新的Fragmen和事务 Fragment newFragment = new ExampleFragment(); FragmentTransaction transaction = getFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, //使用这个Fragment替换在Fragmen容器中的Fragmet // and add the transaction to the back stack //添加这个事务到返回栈 transaction.replace(R.id.fragment_container, newFragment); transaction.addToBackStack(null); // Commit the transaction //提交这个事务. transaction.commit();
在这个例子中,新的Fragmen替换了R.id.fragment_container ID指定的布局容器中当前存在的fragment(如果存在的话).通过调用addToBackStack()方法,替换事务被保存在了返回栈中,这样用户可以回退这个事务,通过按下返回键返回到以前的fragment.
如果你在事务中添加了多个变化(比如另一个add()方法或者remove()方法),然后调用了addToBackStack()方法,那在你调用commit()方法之前的所有变化都会作为单独的事务被添加到返回栈中,返回键将会把他们全部回退.如果你在向同一个容器添加多个fragment,那么你添加的顺序决定了他们在视图层出现的顺序.
如果在你执行一个移除所有fragment的事务的时候没有调用addToBackStack()方法,那么这个fragment将会在事务提交后被摧毁,用户不能再返回到之前的fragment.如果你在移除fragment的时候调用了addToBackStack()方法,那这个 fragment会被停止,并可以在用户按返回的时候恢复.
小贴士:对于每个fragment事务,你可以在提交之前通过调用setTransition()来应用一个fragment动画.
调用commit()方法不能立即执行事务而是安排它运行在Activity的UI线程中("主"线程)---如果这线程可以这么做的话.如果需要,你可以在你UI线程中调用executePendingTransactions()方法来直接执行commit()方法提交的事务.这么多一般不必要除非事务依赖于其他线程的工作.
注意:你只可以在Activity保存他状态之前(在用户离开这个Actvity的时候)使用commit() 方法来提交一个事务.如果你在这个时间点之后提交,系统会抛出一个异常.这是因为如果Activity需要恢复,在提交之后的状态可能会丢失.对于允许丢失提交的情况,请使用commitAllowingStateLoss()方法.
Communicating with the Activity-与Activity的通讯
fragment可以通过getActivity()方法来访问Activity实例并可以轻易的执行像在activity视图中查找View的任务.
View listView = getActivity().findViewById(R.id.list);
使用findFragmentById()或findFragmentByTag()通过从FragmentManager获取一个对这个Fragment的引用,你的Activity可以调用fragment中的方法
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
Creating event callbacks to the activity-为Activity创建时间回调
在一些情况下,你可能需要一个Fragment和Activity共享事件.一个好的方法是在Fragment中定义一个回调接口然后让承载他的Activity实现它.当Activity通过接口接收到调用时,必要时可以和视图中的其他Fragment共享信息.
举个例子,如果一个新的应用在一个Activity中有两个Fragment,一个显示一列文章标题(FragmentA),另一列显示文章内容 (FragmentB),那么在一列被选中的时候,FragmentA必须告诉Actvity那一列被选中了,这样Actvity就可以告诉 FragmentB显示哪一篇文章.在这种情况下,OnArticleSelectedListener 接口会在FragmentA中声明.
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... }
然后承载Fragment的Activity实现OnArticleSelectedListener接口并重写onArticleSelected() 方法来通知FragmentB响应FragmentA的事件.为了保证这个Activity实现了这个接口,FragmentA的onAttach()方法(系统在添加Fragment到这个Activity的时候调用)通过把Activity参数传递到onAttach()方法传递实例化一个 OnArticleSelectedListener实例.
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"); } } ... }
如果Activity没有实现这个接口,那么Fragment会抛出ClassCastException异常.上面的成功例子中,mListener成员有一个Activity实现的OnArticleSelectedListener的引用.这样FragmentA可以通过调用 OnArticleSelectedListener接口定义的方法来共享事件.比如:如果FragmentA是listFragment的扩展,用户每次点击list的一项,系统会调用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); } ... }
Adding items to the Action Bar-在Action Bar上添加项
你的Fragment可以通过实现onCreateOptionsMenu()来为Activity的Options Menu创建菜单项(结果就是形成ActionBar).为了让这个方法接收到调用,你必须在onCreate()方法中调用 setHasOptionsMenu()方法来表明这个Fragment允许在Options Menu中增加项(否则,Fragment将不能接收onCreateOptionsMenu()的调用).
你从Fragment 添加到 Options Menu的任何项都是现存菜单项的附加项.在一个菜单项选中的时候,Fragment也接收响应onOptionsItemSelected()方法的调用.
你也可以在你的Fragment视图中通过调用registerForContextMenu()方法来注册一个视图,从而提供一个上下文菜单.当用户打开上下文菜单时,Fragment会接收一个onCreateContextMenu()的调用,当用户选择一项的时候,Fragment接收一个 onContextItemSelected()的调用.
注意.即使你的Fragment在每个添加的菜单项接收了一个on-item-selected调用,在用户选择一个菜单项的时候,Activity是第一个接受各自调用的组件.如果Activity实现的on-item-selected调用没有处理选择项后的事件,那这个事件会传递到Fragment的回调中.这对 Options Menu 和上下文菜单都是适用的.
Handling the Fragment Lifecycle-处理Fragment的生命周期
处理Fragment的生命周期和处理Activity的生命周期很相似.和Activity一样,Fragment的生命周期有一下三个状态:
Resumed
- Fragment在运行中的Activity中可见
Paused
- 另一个Activity在前台或者获得了焦点,但是Fragment所在的Activity仍然可以看到(可能是前台Activity占据了屏幕的一部分或者是半透明的)
Stopped
- Fragment不可见.宿主Activity可能已经被停止了或者这个Fragment已经从这个Activity中移除了并被添加到了返回栈.一个停止的Fragment仍然是存活的(所有的状态和成员信息被系统保存着).然而他不再对Activity可见,如果宿主Activity被杀死了,他也会被杀死.
和Activity一样,在这个Activity所在的进程被杀死或者你需要在Activity重新创建的时候保存Fragment的状态,你可以用Bundle来做这个工作.你可以在Fragment执行onSaveInstanceState()方法的时候保存它的状态,然后在 onCreate()或者onCreateView(),onActivityCreated()方法的时候恢复这些状态.
Activity和Fragment最大的不同是他们在返回栈中的存在形式.默认的,Activity在停止的时候,是放在一个被系统管理的返回栈中(这样用户可以使用back按钮返回).然而在一个移除Fragment的事务中,只有在你通过调用addToBackStack()明确的指明这个 Fragment要被保存,这个Fragment才会被放在被宿主Activity管理的返回栈中.
另外,管理Fragment的生命周期和管理Activity的生命周期很相似.所以,当管理Activity生命周期的方法也适于管理Fragment的生命周期.当然你也需要明确Activity对Fragment生命周期的影响.
注意,如果在你的Fragment中需要一个context对象,你可以调用getActivity.然而,只有这个Fragment附在这个Activity上的时候,才可以调用getActivity().如果Fragment还没有附加在Activity 上,或者在最后的生命周期和Activity分离了,那getActivity()方法将会返回null.
Coordinating with the activity lifecycle-和Activity生命周期的协调
拥有Fragment的Activity的生命周期会直接影响Fragment的生命周期,每个Activity生命周期方法会影响到每个 Fragment.举个例子,当一个Activity执行onPause()方法的时候,它里面的每个Fragment也会执行onPause().
Fragment有一些额外的生命周期,用来处理和Activity的特殊交换,从而可以执行形如创建和销毁FragmentUI的事情.这些额外的回调方法有:
onAttach()
- 当Fragment和Activity链接起来的时候调用(Activity在这里传送过来).
onCreateView()
- 创建Fragment的视图层.
onActivityCreated()
- 当Activity的onCreate返回的时候执行.
onDestroyView()
- 当Fragment的试图层被移除的时候执行.
onDetach()
- 当Fragment和Activity分离的时候执行.
一旦Activity到达了resume状态,你可以随意添加和移除Activity中的Fragment.当然,只有这个Activity在resume状态的时候,Fragment的生命周期才可以独立的变化.
然而,当activity离开了resume状态,Fragment会再一次被activity推到它的生命周期中.
Example-例子
为了把上面介绍的知识汇总,这里有个使用两个Fragment组成两个视图布局的例子.下面的activity包含两个Fragment,一个用来显示Shakespeare话剧的标题,另一个用来显示选中话剧的简介.也演示了怎么根据屏幕的不同为这两个Fragment提供不同的配置.
注意:完整代码在FragmentLayout.java中.
主activity用平常的方式生成布局,在onCreate()方法的时候:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.fragment_layout); }
fragment_layout.xml如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"> <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment" android:id="@+id/titles" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" /> <FrameLayout android:id="@+id/details" android:layout_weight="1" android:layout_width="0px" android:layout_height="match_parent" android:background="?android:attr/detailsElementBackground" /> </LinearLayout>
直到用户选择了标题一个Fragment才会被放到FrameLayout.
然而,不是多有的屏幕配置都足够显示这两个Fragment视图.按照res/layout-land/fragment_layout.xml文件,上面的布局只适合横屏.
那么当屏幕在竖屏的时候,系统会使用下面的布局,保存在res/layout/fragment_layout.xml中.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width"match_parent"> <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment" android:id="@+id/titles" android:layout_width"match_parent" /> </FrameLayout>
这个布局包含TitlesFragment.这意味着当设备在竖屏的时候,只有话剧的标题是可见的.所以,当用户点击列表的一项的时候,应用将会开始一个新的activity来显示简介而不是载入第二个Fragment.
接下来,你将看到这在Fragment类中是怎么实现的.首先是TitleFragment,显示了莎士比亚话剧的标题.这个Fragment继承自ListFragment,可以通过它实现大多数显示列表信息操作.
正如你看到的那样,注意在用户点击列表项的时候,有两个可能的行为:如果这两个视图存在,将在这个activity中创建并显示一个新的 Fragment(把Fragment添加到FragmentLayout中);如果只有一个视图(竖屏),那会启动一个新的 activity(Fragment在这个activity中显示).
public static class TitlesFragment extends ListFragment { boolean mDualPane; int mCurCheckPosition = 0; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Populate list with our static array of titles. setListAdapter(new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES)); // Check to see if we have a frame in which to embed the details // fragment directly in the containing UI. View detailsFrame = getActivity().findViewById(R.id.details); mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE; if (savedInstanceState != null) { // Restore last state for checked position. mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); } if (mDualPane) { // In dual-pane mode, the list view highlights the selected item. getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); // Make sure our UI is in the correct state. showDetails(mCurCheckPosition); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", mCurCheckPosition); } @Override public void onListItemClick(ListView l, View v, int position, long id) { showDetails(position); } /** * Helper function to show the details of a selected item, either by * displaying a fragment in-place in the current UI, or starting a * whole new activity in which it is displayed. */ void showDetails(int index) { mCurCheckPosition = index; if (mDualPane) { // We can display everything in-place with fragments, so update // the list to highlight the selected item and show the data. getListView().setItemChecked(index, true); // Check what fragment is currently shown, replace if needed. DetailsFragment details = (DetailsFragment) getFragmentManager().findFragmentById(R.id.details); if (details == null || details.getShownIndex() != index) { // Make new fragment to show this selection. details = DetailsFragment.newInstance(index); // Execute a transaction, replacing any existing fragment // with this one inside the frame. FragmentTransaction ft = getFragmentManager().beginTransaction(); if (index == 0) { ft.replace(R.id.details, details); } else { ft.replace(R.id.a_item, details); } ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); ft.commit(); } } else { // Otherwise we need to launch a new activity to display // the dialog fragment with selected text. Intent intent = new Intent(); intent.setClass(getActivity(), DetailsActivity.class); intent.putExtra("index", index); startActivity(intent); } } }
第二个Fragment,DetailsFragment显示了在TitleFragment中选中的话剧简介.
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; } }
如果用户点击列表项的时候当前布局不包含R.id.details视图(DetailsFragment 所在的视图),那应用将会启动DetailsActivity 来显示选中项的内容简介.
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会在横屏的时候结束自己,这样主activity可以接管并显示DetailsFragment旁边的TitlesFragment.如果用户在竖屏的时候启动DetailsActivity,然后把设备转到横屏,将会重启当前的activity.