Fragments

来源:互联网 发布:站内优化和站外优化 编辑:程序博客网 时间:2024/05/22 14:05

一个Fragment代表一个行为或者界面的一部分在Activity。你可以混合多个片段在一个activity中创建一个多窗口ui并且多次使用一个片段在多个activity。你可以认为一个fragment是activity模块的一部分,它有自己的生命周期,接收他自己的事件并且可以增加或移除当activity在运行的时候。

         一个片段必须要嵌入在一个activity中并且片段的生命周期直接被主activity的生命周期影响。比如,当一个activity暂停时,所有的片段在里面,并且当activity销毁的时候,所有的fragment也一样。但是当一个activity运行的时候(他会在resumed的生命周期状态),你可以独立操作每个fragment,比如增加或移除他们。当你执行一个fragment事务时,你可以增加它到一个返回栈这个被activity管理的,每个返回栈在activity记录着发送的fragment事务。这个返回栈允许你倒退一个fragment事务,当你按返回按钮。

         当你增加一个fragment作为你activity布局的一部分,他存在一个ViewGroup中在activityview层中并且fragment定义自己的视图布局。你可以插入一个fragment在你的activity布局中被描述的fragment在activity的布局文件里。作为一个<fragment>元素,或者一个fragment是activity需要的一部分;你可以使用一个fragment没有没有自己ui作为一个不可见的工作者为activity。

         这个文档描述怎么样建立你的应用使用fragment,包括怎么维持他们的状态当增加到activity返回栈中,分享事件和activity并和另外的fragment在activity中,贡献一个activity操作条,或者更多。

 

设计原理

Android介绍fragment在android3.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组合为不同的屏幕尺寸。当设计你的应用支持平板或手持设备,你可以重新使用你的fragments在不同的布局配置来优化用户的体验基于可用的屏幕空间。比如一个手持设备,他需要独立分段提供一个单个的UI当更多不能适合同一个activity。

比如继续是一个新闻应用,这个应用嵌入两个fragment在ActivityA,当运行在平板大小的设备上。但是,在手持设备大小的屏幕,这里没有足够的空间给两个fragment,所以ActivityA包括自由一个fragment显示文章列表,并且当用户选择一个文章时,他会启动activity b,这个包含了第二个fragment来阅读文章。因此,这个应用支持平板和手持设备用可以复用的frgment一不同的组合,如图表1.

更多的信息关于设计你的应用用不同的fragment组合为不同的屏幕配置,看Supporting Tablets and Handsets.

 

创建一个fragment

创建一个fragment,你必须继承Fragment类。这个Fragment类代码有点像一个Activity。他包含了回调方法像一个activity,比如onCreate,onStart,onPause,和onStop。事实上假如你使用fragment,你只需要简单的移动代码从你的activity回调函数到各自fragment的回调方法。

通常,你必须实现下面的生命周期方法。

onCreate:这个是系统调用哪个当你创建一个fragment的时候。在你的实现里,你应该初始化基本的组件你需要维持的当fragment被暂停或停止,或恢复。

onCreateView:这个系统调用当这个fragment第一次去绘制他用户界面。绘制一个UI为fragment,你必须返回一个View从这个方法里这个是fragment的根布局。你尅返回null假如fragment没有提供UI。

onPause:这个系统调用这个方法作为第一个迹象用户离开这个fragment(虽然它不是意味着fragment被销毁)。这个通常你需要提交各个改变这些被持久化当前用户的会话(因为用户也许不会回来了)

 

许多应用实现至少这三个方法为每个fragment,但是这些其他的回调函数你应该使用在不同的生命周期阶段。所有的生命周期返回方法被讨论在Handing the Fragment Lifecycle里。

 

DialogFragment:展示一个浮动的对话框。用这个类可以创建一个对话框使用对话框辅助方法在Activity类中,因为你可以包含一个fragment对话框在你的fragment回收栈里被activity管理。允许用户返回一个解散的fragment。

ListFragment:展示一个列表管理用一个适配器(比如SimpleCursorAdapter),等同于ListActivity。者提供了多个方法管理列表,比如onListItemClick回调执行点击事件。

PreferenceFragment:展示一个Preference层对象作为一个list,像PreferenceActivity。这个有用当创建一个设置activity对你的应用。

 

增加一个用户界面

一个fragment通常是activity界面的一部分和贡献自己的布局给这个activity。

为了提供一个fragment布局,你必须实现onCreateView回调函数,这个android系统自己调用当fragment在绘制布局的时候。你实现了这些方法返回一个View这个是fragment的根布局。

注意:假如你的fragment是ListFragment的子类,这个默认实现返回一个ListView从onCreateView,这个你不需要实现它。

返回一个布局从onCreateView(),你可以填充它从一个布局的资源。为了帮助你所以onCreateView提供了一个LayoutInflater对象。

这个container参数传递onCreateView是父亲布局是你fragment对象插入的布局。这个有个saveInstanceState参数是Bundle提供数据是上一个实例化Fragment,假如fragment被恢复。

这个inflate方法带了三个参数:

1.      你需要填充的布局资源的id。

2.      是填充布局的父亲。通过container十分重要为了系统布局参数为了填充的根布局,指定父亲布局.

3.      一个布尔值指向是否布局需要附加在viewGroup上(第二个参数),在填充中.(这这种情况下,这里是false因为系统已经将填充的布局插入到container)

 

现在你看怎样创建一个fragment提供一个布局,节下来你需要增加你的fragment到你的activity中。

 

增加一个fragment到activity中

通常一个fragment包含UI的一部分对主的activity,这个是插入作为activity的一部分视图层。这里有两种方式增加一个fragment在activity布局。

*.描述fragment在activity布局文件中

这种中情况,你可以指定一个布局属性为这个fragment作为它的一个view,比如

Android:name属性指定一个Fragment类实例在这个布局中。

当系统创建这个activity布局时,他会实例化每个指定的fragment在布局中并且调用onCreateView为每个布局,检索每个fragment布局。这个系统直接插入这个返回的view在<fragment>元素中。

注意:每个fragment需要提供唯一的表示系统可以使用它恢复fragment假如activity被重启(并且当你可以使用这个fragment的能力来执行事务,比如移除它)。这里有三个id给fragment

1.      支持android:id一个唯一的id

2.      支持android:tag属性一个唯一的字符串

3.      假如你没有提供前两个,系统使用container的id

 

或者程序增加一个已经存在的viewGroup。

在activity运行的任何时候,你可以增加一个fragment在你的activity布局中。你可以简单的指定一个viewGroup一个可以放置fragment。

为了fragment事务在你的activity(比如增加,移除,代替),你可以使用api从FragmentTransaction。你可以获取一个FragmentTransaction从你的activity

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在哪个fragment需要被放置,指定一个资源的id,第二个参数是加入的fragment。

一旦你改变你用FragmentTransaction,你必须调用commit来改变。

 

增加一个没有UI的fragment。

这个例子在增加一个fragment未你的activity为了提供UI。但是,你可以使用一个fragment提供在后台操作为activity不提供额外的UI。

增加一个没有UI的fragment,增加这个fragment从activity使用add(Fragment,String)(支持一个唯一的字符串为这个fragment,而不是一个view ID)。增加fragment,但是因为他没有联系到一个视图在一个activity上,他不接收onCreateView的调用。所以你不需要实现这个方法。

支持一个字符串为这个fragment不是严格为这个没有UI的fragment,你可以支持一个字符串对这个fragment做有一个UI但是假如fragment没有一个UI,然后这个字符串是唯一指向它,假如你想获取fragment从一个activity,你需要使用findFragmentByTag()。

比如一个例子activity使用一个fragment作为一个后台工作者没有UI,看FragmentRetainInstance.java例子。

 

管理fragment

为了管理fragment在你的activity里,你必须使用FragmentManager。可以调用getFragmentMagager方法在你的activity中。

一些东西你可以做用FragmentManager:

1.      获取fragment已经存在在一个activity中,使用findFragmentById()(为fragment提供一个UI在一个activity布局里)或者用findFragmentByTag(为fragment有或没有提供UI)

2.      弹出fragment在返回栈中,用popBackStack()(模拟一个返回按钮)

3.      注册一个监听为返回栈中改变,用addOnBackStackChangedListener().

为更多的信息关于方法和另外的,参考FragmentManger类文档。

作为演示在前一部分,你可以使用FragmentManager打开一个FragmentTransaction。这个允许你执行事务比如增加或者移除一个fragment。

执行fragment事务

一个重要的特性关于使用fragment在你的activity中是有能力增加移除,替换和执行另外的操作,来响应用户交互。没一个设置的改变你提交给activity需要调用一个事务,你可以执行一个使用api在FragmentTransaction。你可以保存每个事务在返回栈管理被activity,允许用户导航防护通过fragment的改变。(类似于activity的导航返回)。

你可以获取一个实例FragmentTransaction从一个FragmentManager像,

FragmentManager fragmentManager = getFragmentManager();FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每一事务设置改变你需要执行在同一时间,你可以设置所有的改变。你可以执行一个给定的事务用方法add,remove,replace。然后应用事务到activity,你必须调用commit。

在你调用commit,然而你也许想调用addToBackStack,为了增加一个事务一个返回栈的事务。这个返回栈被activity管理并且运行用户返回上一个fragment状态用按back按钮。

比如,这里你可以代替一个fragment用另外一个,显示上一个状态在返回栈中。

// Create new fragment and transactionFragment 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代替无论什么fragment当前在布局容器知道R.id.fragment_container ID。调用addToBackStack,这个代替事务保存在返回栈中所以用户可以回复事务并且带回上一个fragment在按返回按钮。

假如你增加多个改变对事务(比如另外一个add()或者remove())并且调用addToBackStack(),然后所有的改变被提供在你调用commit被增加到backstack作为一个单独的事务并且返回按钮返回他们一起。

这个不是很要紧你增加改变的顺序,除了:

1.      最后你必须调用commit。

2.      假如你增加多个fragment在同一个容器内,然后这个顺序在你增加的时候就决定了你显示层次的顺序。

假如你不调用addToBackStack当你执行事务移除fragment,然后fragment销毁当事务都提交并且用户不能导航回它。但是你调用addToBackStack当移除一个fragment,让回fragment被停止并且被恢复假如用户使用返回按钮。

 

小提示:为每个fragment事务,你可以使用一个过渡动画调用setTransition在你提交前。

 

调用commit不是马上执行事务。当然,他执行运行在UI线程中一旦这个线程允许这么做。你需要,但是你也许可以调用executePendingTransactions从你的UI线程马上执行事务提交用commit。做这些通常不是必须的,除非事务依赖工作在另一个线程。

注意:你可以提交一个事务用commit在activity保存它的状态前(当用户离开这个activity)。假如你想提交在某个点,一个异常就会被抛出。这个是因为状态在被提交后可能会丢失假如activity需要被存储。在这个问题上可以丢失提交使用commitAllowingStateLoss.

 

和activity的交互

虽然一个Fragment实现作为一个对象独立一个与activity并且可以被使用在多个activity中,一个给定的fragment实例直接的绑定在activity。

特别,这个fragment可以用getActivity来获取activity实例并且容易执行一个找到一个视图在activity中。

View listView = getActivity().findViewById(R.id.list);

否则,你可以调用方法在一个fragment获取一个引用对这个Fragment从FragmentManager,使用findFragmentById()或者findFragmentByTag,比如

ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);

创建一个时间回调到activity中。

在一些情况,你需要一个fragment分享事件和activity,一个很好的方式时定义一个回调的接口在fragment并且要求主activity实现它。当activity接收一个回调函数通过一个接口,他可以分享信息和另外的fragment在不同的布局中。

比如一个新的应用又两个fragment在一个activity中一个展示文章列表并且另一个展示文章然后fragmentA必须告诉tell这个activity当一个列表被选择因此它能告诉B展示这篇文章,在这种情况下,OnAtricleSelectedListener接口被描述在FragmentA:

public static class FragmentA extends ListFragment {    ...    // Container Activity must implement this interface    public interface OnArticleSelectedListener {        public void onArticleSelected(Uri articleUri);    }    ...}

然后这个activity持有的主fragment实现了OnArticleSelectedListener接口并且覆盖onArticleSelected来通知fragmentB事件从fragment A。确认这个主activity实现这个接口,fragmentA的onAttach回调方法(这个系统调用当增加一个fragment到一个activity中)实例化一个OnArticleSelectedListener被投掷到activity这个传递在onAttach()

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抛出一个异常,当成功,这个mListener成员变量持有一个引用activity实现的OnArticleSelectedListener,所以fragmentA可以分享事件和activity调用方法被OnArticleSelectedListener接口定义的。举个例子,假如一个fragmentA是继承了ListFragment,每次用户点击一个列表项目,这个系统就会调用onListItemClick在fragment,这个调用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);    }    ...}

这里id参数传递给onListItemClick是每一行点击的id,这个activity使用来获取article从一个应用的ContentProvider。

 

增加一个项目在Action bar上

你的fragment可以贡献菜单选项给activity的Options Menu实现onCreateOptionsMenu().为这个方法接收调用,然而你必须调用哪个setHasOptionsMenu在onCreate里,这个指向fragment可以增加项目在Options Menu(否则,这个fragment将不再接收调用onCreateOptionsMenu())。

任何一个项目你然后增加OptionMenu从fragment被附加在已经存在的菜单栏目中。这个fragment接收回调对onOptionsItemSelected当一个菜单被选择。

你可以注册一个视图在你的fragment布局中提供一个菜单调用哪个registerForContextMenu。当用户打开上下文菜单,这个fragment接收一个调用onCreateContextMenu,当用户选择一个item,这个fragment接收一个调用onContextItemSelected()

注意:虽然你的fragment接收一个选项的选择回调函数为每个菜单它做增加的,activity首先接收各自的回调的那个用户选择一个菜单栏目。假如activity实现一个栏目选择回调函数不执行这个栏目,然后事件传递给fragment回调函数,这个返回true为OptionsMenu 和上下文环境。

 

管理fragment生命周期像管理一个activity的生命周期。像一个activity,一个fragment可以有三个状态。

Reumed:fragment在运行的activity中可见。

Paused:另一个activity在前端并且有焦点,但是activity包含离开的fragment仍然可见(这个前端activity时一步分透明后者没有盖住整个屏幕)

Stopped:这个fragment不可见,主activity停止或者fragment被移除从activity中但是增加在返回栈中。一个停止的fragment还是活着(所有状态和成员信息都被保留在系统中)。然而,他不是可见对用户并且被销毁假如activity被销毁。

 

也像一个activity,你可以保留一个fragment的状态用bundle,万一activity进程被杀并且你需要恢复这个fragment的状态当activity重新被创建。你可以保存状态在fragment的onSaveInstanceState回调函数并且恢复它在onCreate,onCreateView,或者onActivityCreated。

最象征不同的生命周期在activity和fragment是一个存储在各自的返回栈中。一个activity被放置在一个activity返回栈是有系统管理的当它停止(所以用户可以使用它用返回按钮)。但是一个fragment是放置在一个主activity管理的返回栈中当你明确请求实例被保存调用addToBackStack在移除事务期间。

否则管理fragment生命周期和activity生命周期的管理是一样的。所有,一些管理activity生命周期的练习同样适用于fragment。什么你需要明白的,通过activity的生命影响到fragment生命。

注意:假如你需要一个Context对象在你的fragment,你可以调用getActivity。然而,需要注意调用getActivity只有在fragment附加到activity上。当fragment没有附加上,或者分离在生命的最后的生命周期,将会返回null。

 

协调activity生命周期

Activity的生命周期会直接影响fragment的生命周期,比如每个生命周期回调为activity的结果同样的回调在每个fragment。比如当你activity接收到onPause,每个fragment在activity中会接收onPause。

Fragment有一些额外的生命周期回调函数,但是他们执行单一的交互和activity为了执行动作,比如创建和销毁fragment的UI。这些附加的回调方法有:

onAttach:调用当一个fragment联系到一个activity上。

onCreateView:调用创建一个视图层在fragment。

onActivityCreated:调用当onCreate方法返回。

onDestroyView:调用当视图层被移除

onDetach:调用当fragment被分离从activity中。

 

这些流动的fragment生命周期,他们都会被主的activity影响,实例看图3。在这个图中,你可以看到怎么样每个连续的状态决定哪些fragment回调。举个例子,当activity接收到onCreate方法,一个fragment在activity中接收不会超过onActivityCreated回调函数。

一旦activity到达恢复状态,你可以自由的增加和删除一个fragment。因此只有在activity在恢复的状态下fragment的状态可以被自由的改变。

但是,当activity离开恢复状态,fragment推倒他的生命周期被activity。