Fragment代表了在Activity中的一种或者一部分行为,你可以在单个的activity中连接多个fragment来构建一个多面板的UI,并且在多个activity中重复使用一个fragment,你可以把fragment想象成为activity的一个模块化片段,它有它自己的生命周期,接收它自己的输入事件,还有就是当activity运行的时候,你也能添加或者移除fragment。
一个frgment必须被嵌入在一个activit中,并且这个fragment的生命周期直接被它的主activity的生命周期所影响。例如,当activity被暂停的时候,在它里面的fragment也会被暂停,当activity被销毁的时候,在它里面的fragment也会被销毁。但是,当activity运行的时候(它恢复了生命周期状态),你可以独立的维护每一个fragment,比如添加或者移除他们。当你执行这样一个fragment事件的时候,你可以把它添加到被这个activity所管理的回退栈中,在activity中的每一个回退栈记录了fragment的事件的发生。通过按返回按钮,回退栈允许用户返回一个fragment事件(向后导航)。
当你添加的fragment作为activity布局的一部分的时候,它就寄居在activity的视图结构的一个ViewGroup中,而且fragment定义了它自己的布局。你可以在activity的布局文件中定义fragment来插入到你的activity中,像<fragment>元素,或者通过你的应用程序中的代码把它添加到已经存在的ViewGroup中。然而,一个fragment不一定非要作为activity布局的一部分,你也可以使用一个没有UI的fragment在activity中作为一个不可见的工作者。
这个文档描述了怎样用fragment构建你的应用程序,包括当fragment被添加到activity的回退栈的时候,fragment如何维护他们的状态,还有跟activity共享事件以及在activity中的其它fragment,贡献给activity的动作条等等。
设计哲学
Android在3.0中引入了fragment,主要是为了在大屏上支持更多的动态的和固定UI设计,像平板。由于平板的屏幕比手持设备的要大,也就有更多的空间链接并且与UI组件交互了。像这样的fragment设计让你避免了管理复杂的视图结构。通过把activity的布局分隔为fragment,你能够在运行时修改activity的外观,并且会在activity管理的回退栈中维持这些改变。
例如,一个新闻应用程序可以使用一个fragment在左边展示一个文章列表,另一个fragment在右边展示文章,这两个fragment都出现在一个activity中,紧挨着,并且每一个fragment有它自己的生命周期回调方法还有它们自己的用户输入事件。因此,代替使用activity选择一个文章,并且另一个activity来读这个文章,用户能在同一个activity中选择文章并且读文章。在平板中的插图如图:
你应当把fragment设计成为一个模块化的并且可以重复使用的activity组件。也就是说,由于每一个fragment定义了它自己的布局以及用它自己的生命周期回调定义了自己的行为,你可以在多个activity中包含一个fragment。这是非常重要的,因为一个模块化的fragment允许你改变你的fragment来连接不通尺寸的屏幕。当设计你的应用程序既支持平板又支持手持设备的时候,你可以基于有效的屏幕空间在不同的布局配置中重复使用你的fragment来优化用户体验。例如,在一个手持设备上,当超过一个fragment的时候在activity中不能适配,也许就需要分隔fragment来提供一个单个的UI。
例如-继续使用新闻应用程序例子-当运行在一个平板电脑大小的设备上的时候,在activity A中,应用程序可以嵌入两个fragment。但是在一个手持屏幕上,没有足够的空间给两个fragment,因此Activity包含唯一的fragment给文章列表,而且当用户选择了一个文章的时候,它开启了Activity B,这个activity包含了第二个fragment来读这个文章。因此用不同的组合通过重复使用fragment,应用程序既支持平板也支持手持。
用不同的fragment组合给不同的屏幕配置设计你的应用程序的更多信息,请看 Supporting Tablets and Handsets。
创建一个Fragment
为了创建一个Fragment,你必须实现创建一个Fragment的子类(或者它的子类的子类)。Fragment的代码跟activity的看起来很像。它包含的回调方法跟Activity也很相似。像onCreate(),onStart(),onPause()还有onStop()。事实上,如果你使用fragment来转换一个已经存在的Android应用程序,你可以从你的activity的回调方法中简单的把代码移动到相应的fragment的回调方法中。
通常,你应该至少实现生命周期中的下面三个方法:
onCreate()
当创建fragment的时候系统调用。在你的实现里,你应该初始化fragment需要的组件,这个组件是当你的fragment被暂停或者停止的时候被回收的,然后恢复。
onCreateView()
当fragment第一次绘制它的UI的时候系统调用它。为了给你的fragment绘制UI,你必须从这个方法中返回你的fragment布局的根View。如果fragment不提供UI,你可以返回null。
onPause()
当检测到用户要离开这个fragment的时候系统调用它。这个方法应该通过当前用户session是通常你应该提交应该持久化的数据的地方。
大多数的应用程序为每个fragment至少应该实现那三个方法,但是有几个其它的回调方法你也应该用于操作fragment生命周期的不同阶段。 所有的生命周期回调方法被详细的讨论在 Handling the Fragment Lifecycle。
也有几个可能你想扩展的子类代替Fragment基类:
DialogFragment
展示一个浮动的对话框。使用这个类创建对话框,在Activity类中使用对话框帮助器方法是一个不错的选择。因为你可以把一个fragment对话框合并到被activity管理的回退栈中。allowing the user to return to a dismissed fragment(这里先pending)。
ListFragment
展示一个被适配器管理的项列表(像SimpleCursorAdapter),与ListActivity是相似的。它提供了几个方法来管理列表视图,像onListItemClick()回调来持有点击事件。
PreferenceFragment
展示一个Preference对象的结构列表,跟PreferenceActivity相似。当在你的应用程序中创建一个“settings”的activity是有效的。
添加一个用户接口
一个fragment通常被用于activity的用户接口的一部分而且把自己的布局提供给activity。为了给一个fragment提供一个布局,你必须实现一个onCreateView()回调方法,当fragment绘制它的布局的时候,这个方法被调用,你的这个方法的实现必须返回你的fragment的布局的根View。
注意:如果你的fragment是ListFragment的子类,从onCreateView()中默认返回一个ListView,因此你不需要实现它。
为了从onCreateView()中返回一个布局,你可以从一个被定义在XML中的布局资源填充它。为了帮助你做这些,onCreateView()提供了一个LayoutInflater对象。
例如,这是个Fragment的子类从example_fragment.xml文件中载入一个布局:
public static class ExampleFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.example_fragment,false); } }
被传递给容器的参数是父ViewGroup(来自于Activity的布局),在这个ViewGroup中,你的fragment布局被插入。参数savedInstanceState是一个Bundle,如果fragment被恢复,这个对象提供了前面的fragment的实例的数据。
inflate()带有三个参数:
你想要填充的布局的ID
ViewGroup是被填充布局的父ViewGroup,对被填充的即将要进入的被指定的父视图布局的根视图为了让系统应用布局参数,传递container是重要的。
一个布尔值,检查是否在填充期间被填充的布局应当被绑定到ViewGroup(第二个参数)。
现在你已经看到了提供一个布局如何创建一个fragment。接下来,你需要把fragment添加到你的activity中。
在activity中添加一个fragment
通常,fragment给主activity提供了一部分的UI,它作为activity所有视图结构的一部分被嵌入。有两种方式你能给activity布局添加一个fragment。
在activity的布局文件中定义fragment。
在这个例子中,你能给fragment像视图一样指定布局属性。例如,下面的布局文件中两个fragment组成了一个activity:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns: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:name="com.example.news.ArticleReaderFragment" android:id="@+id/viewer1" android:layout_weight="2" android:layout_width="0dp" android:layout_height="match_parent" /> </LinearLayout>
在<fragment>中的android:name属性指定了在布局中要实例化的Fragment类。当系统创建这个activity布局的时候,它初始化每一个在布局中指定的fragment,并且给每一个fragment调用onCreateView()方法来获取每一个fragment的布局。系统插入被fragment直接返回的<fragment>元素的地方的视图。
注意:如果activity被重启(并且你能用它获取fragment来执行事物,像移除它),每一个fragment要求有一个唯一的系统可以用来恢复fragment的标示符。有三种方式可以给fragment提供一个ID:
提供android:id属性支持一个唯一ID
提供android:tag属性支持唯一ID
如果你没有提供前面的两个属性,系统会使用容器视图ID。
或者,编程式的给已经存在的ViewGroup添加fragment
在任何时候,当你的activity运行的时候,你可以给你的activity布局添加fragment。你只需要在这个activity中指定一个放置fragment的ViewGroup。
为了在你的activity中制造fragment事物(像添加,移除,替换一个fragment),你必须使用FragmentTransaction的API。你可以从你的Activity中获得FragmentTransaction的实例,像这样:
FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
然后你可以使用add()方法添加一个fragment,指定fragment去添加,并且使用这个视图插入它。例如:
ExampleFragment fragment = new ExampleFragment(); fragmentTransaction.add(R.id.fragment_container,fragment); fragmentTransaction.commit();
传递给add()的第一个参数是通过指定的资源ID放置frament的ViewGroup,并且第二个参数是要被添加的fragment。
一旦你用FragmentTransaction做了改变,你必须调用commit()来让改变生效。
添加一个没有UI的fragment
上面的例子展示了如果给你的activity添加一个fragment来提供一个UI。然而,你可以使用一个fragment在没有额外的UI的情况下给这个activity提供一个后台行为。
为了添加一个没有UI的fragment,使用add(Fragment,String)从activity中添加fragment(给fragment提供唯一的字符串“tag”,而不是一个视图ID)。这就添加了fragment,但是,由于它在activity布局中没有跟一个视图关联,它也就不会接收一个onCreateView()的调用。因此你不需要实现这个方法。
给非UI的fragment提供一个字符串标签不是强制的,你也能给有UI的fragment提供字符串标签-但是如果fragment没有头UI,那么这个字符串tag就是辨识它的唯一方式。如果你想在以后从activity中获取fragment,你需要使用findFragmentByTag()。
管理fragment
为了管理你的activity中的fragment,你需要使用FragmentManager。为了获取它,从你的activity中调用getFragmentManager()。
你可以用FragmentManager做下面的事情:
用findFragmentById()(对于在activity布局中提供了UI的fragment)或者findFragmentByTag()(给没有提供UI的fragment)获取在activity存在的fragment。
使用popBackStack()从回退栈中弹出fragment。
用addOnBacktackChangeListener()注册一个监听器到回退栈中。
关于那些方法和其它的更多的信息,请看FragmentManager类文档。
像在前面的片段中显示的,你也能使用FragmentManager打开一个fRAGMENTtRANSACTION。这个FagmentTransaction允许你执行像添加和移除frament的事务。
执行Fragment事务
在你的activity中使用fragment的更大的一个特性是在你的用户交互上使用这些fragment能添加,移除,替换和执行其它的动作。每一个你提交到activity的改变都调用了一个事务并且你可以使用FragmentTransaction的API执行。你也可以保存每一个事务到一个被这个activity管理的回退栈,通过fragment的改变允许用户导航回退(类似于通过activity回退)。
你可以从FragmentManager请求一个FragmentTransaction实例,像这样:
FragmentManager fragmentManager = getFragmentManager(); FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每一个事务是一组你想要在同一时间执行的改变。你可以给一个被给定的事务使用像add(),remove()和replace()方法设置所有你想要执行的改变。然后,给这个activity应用事务,你就必须调用commit()。
在你调用commit()方法以前,然后,为了添加事务到一个fragment事务的回退栈中,你也许想调用addToBackStack()。这个后台站被这个activity所管理并且允许用户通过按返回按钮返回到前一个fragment状态。
例如,下面的例子是用一个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 stack transaction.replace(R.id.fragment_container,newFragment); transaction.addToBackStack(null); //Commit the transaction transaction.commit();
在这个例子中,不管在当前的布局文件中的fragment是什么,newFragment会用被标示的R.id.fragment_container的ID替换掉它。通过调用addToBackStack()替换被爆粗你在回退栈中的事务,因此用户可以维持这个事务并且通过按下返回按钮回到前一个fragment。
如果你给事务添加了多个改变(像另一个add()或者remove())并且调用addToBackStack(),那么在你提交以前所有被应用的改变作为一个单独的事务会被添加到回退栈,并且返回按钮将一起回退它们。
你添加到一个FragmentTransaction钟的顺序是无关紧要的,除了:
你必须最后调用commit()
如果你给相同的容器添加多个fragment,那么你添加它们的实训决定了它们在视图结构中出现的顺序。
当你执行一个移除一个fragment的事务的时候,如果你不调用addToBackStack(),那么当事务被提交的时候,那个fragment会被销毁,并且用户就不能导航回到上一个fragment了。反之,当移除一个fragment的时候,如果你调用addToBackStack(),那么fragment背停止并且如果用户导航回退,它将被恢复。
建议:对于每一个fragment事务,在你提交之前,你可以通过调用setTranction()应用事务动画。
调用commit()不会立即执行事务。当然,它会在activity的UI线程中有计划的执行。如果需要,然后,你可以从你的UI线程中调用executePendingTransaction()来立即执行通过commit()提交的事务。这样做通常是不需要的,除非这个事务是一个依赖于在其它线程中的任务的。
注意:你可以使用commit()提交一个事务,这个动作仅仅优先于activity保存它的状态的动作。如果你尝试在那个点之后提交,将会抛出一个异常。这是因为如果activity需要被恢复,提交后的状态可能会被丢失。对于这种情况,你可以使用commitAllowingStateLoss()来记录你丢失的commit。