一个Fragment代表Activity用户界面的一个行为或一部分。可以绑定多个fragment到一个单独的activity来创建多面板的UI,也可以在多个activity中重复使用一个Fragment。可以把Fragment当做activity的拥有独立的生命周期可以接收输入时间的模块区域。可以在activity运行的状态下添加和移除Fragment。
一个Fragment必须被嵌入activity中,并且Fragment的生命周期被主activity的生命周期所影响。例如,如果一个activity被暂停,那么它拥有的所有的Fragment都被暂停。然而,当一个activity运行中(处于Resumed状态),可以独立地操作每个Fragment,例如添加和删除。当执行这样的Fragment事务时,可以将它添加到activity管理着的返回栈中。Activity中的返回栈是Fragment事务记录。返回栈允许通过BACK按钮返回到之前的Fragment事务。
当添加一个Fragment作为activity布局的一部分时,它包含在一个ViewGroup的视图层中。可以在布局中使用<fragment>元素或者在代码中将它添加个一个已经存在的ViewGroup中。
设计思想
Android在3.0版引入Fragment(API对应版本为Honeycomb),主要用来支持在大屏幕上的更多动态的可伸缩的用户界面,例如平板。因为平板的屏幕比手机屏幕大的多,有更大的空间来管理复杂的UI组件。Fragment允许这样的设计而不需要管理视图层复杂的变化。通过将activity的视图分割到Fragment中可以在运行的过程中修改activity的外貌,并且将这些变化保存到返回栈中。
执行Fragment事务
使用Fragment最大的特点是你可以添加、删除、替代以及执行其他的响应用户界面行为。每一个你提交给Activity的变化的集合被称为一个事务,并且可以通过Fragment Transaction的api来执行。你也可以保持每个事务到一个Activity管理着的back栈中,运行用户向后返回操作(类似于Activity的back键行为)。
可以如下获得FragmentTransaction对象
FragmentManager fragmentManager= getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
每一个事务是你想要同时执行的变化的集合。可以通过add(),remove(),replace()方法来设置你想要在一个事务中执行的变化。为了应用事务到activity,必须调用commit()。
在调用commit()之前,可以调用addToBackStack()方法将事务添加到Fragment事务的返回栈(a back stack)中。这个栈被activity管理者,并且允许用户通过按BACK按钮返回到前一个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();
如果你添加多个变化到事务(transaction)中,那么所有在调用commit()方法之前的变化,都会应用到一个同一个事务中。
将变化添加到FragmentTransaction中的顺序不重要但是:
·必须在最后调用commit()方法
·如果你添加多个Fragment到同一个View容器,那么你添加的顺序决定他们显示的View等级
如果不调用addToBackStack(),当执行执行移除Fragment操作时,但移除事务提交以后,Fragment将被销毁,并且用户不可以返回。相反的如果你调用了addToBackStack(),但你移除Fragment时,那个Fragment被停止掉了,当按BACK返回时将被Resume。
提示:对于每一个Fragment事务,你可以在commit()通过调用setTransition()应用一个过渡动画。
调用commit()方法,并不会立即执行事务。而是,降到放到activity的ui线程中去,知道线程去执行。如果必要,可以调用在UI线程中executePendingTransactions()来执行通过commit()方法提交的事务。通常不必这么做,除非那个事务依赖其他线程中的工作。
警告:你只可以在一个activity保持状态之前(当用户离开activity时)提交一个事务,否则会出现异常。因为提交过后的状态可能会丢失,如果activity被重新加载的时候。如果对于丢失也无关紧要的情况,可以调用commitAllowingStateLoss()。
与Activity的通信
尽管一个Fragment是独立于Activity被实现的,并且可以在多个activity中使用,但是一个已经实例化的Fragment对象是被绑定到包含它的activity中的。
特别的,Fragment可以通过getActivity()获得Activity的实例来访问,并且可以容易的执行一些任务,例如获得一个View:
View listView = getActivity().findViewById(R.id.list);
同样的,activity可以调用Fragment中的方法,通过FragmentManager来获得Fragment的引用,使用findFragmentById()或者findFragmentByTag()。例如:
ExampleFragment fragment = (ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);
创建到activity的事件回调
在一些情况下,你可能需要一个Fragment与activity分享事件。一个好的方法是在Fragment中定义一个回调接口,并且要activity来实现该接口。当一个activity从接口接受一个回调,它可以与其他的在那个布局中的Fragment分享信息,如果有必要的话。
例如,如果一个新的应用程序在一个activity中有两个Fragment—一个显示文章标题的列表(Fragment A)另一个显示文章内容(Fragment B)—那么Fragment A必须要告诉Activity当一个列表项被选择的时候,让后告诉Fragment B来显示文章。在这种情况下,OnArticleSelectedListener接口是在Fragment A中声明的:
public static class FragmentA extends ListFragment { ... // Container Activity must implement this interface public interface OnArticleSelectedListener { public void onArticleSelected(Uri articleUri); } ... }
然后拥有那个Fragment的activity实现OnArticleSelectedListener接口,并且重写onArticleSelected()方法将来自FragmentA的事件通知Fragment B。为了保证主activity实现了接口,Fragment A在回调方法onAttach()(系统在将Fragment添加到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成员变量拥有一个实现了OnArticleSelectedListener接口的activity的引用。例如,如果FragmentA是ListFragment的子类,每一个用户点击一个列表项,系统将调用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); } ... }
The id
parameterpassed to onListItemClick() is the row IDof the clicked item, which the activity (or other fragment) uses to fetch thearticle from the application's ContentProvider.
在行为栏(Action Bar)添加项目
Fragment可以通过onCreateOptionsMenu()发布菜单项到activity的选项菜单(包括行为栏)。为了让这个方法接受调用,你必须在onCreate()方法中调用setHasOptionsMenu()来表明Fragment想添加菜单项到选项菜单中(否则,Fragment将不调用onCreateOptionsMenu())。
任何从Fragment中要添加到选项菜单中的项目,都被追加到以及存在的菜单项。在一个菜单项被选中的时候,Fragment也接受onOptionsItemSelected()回调。
也可以在Fragment布局中通过调用registerForContextMenu()注册一个视图来提供上下文菜单。当用户打开上下文菜单时,Fragment接受一个到onCreateContextMenu()的调用。当用户选择上下文菜单的一项时,Fragment接受一个到onContextItemSelected()调用。
注解:尽管Fragment可以接受菜单项被点击的事件,但是activity是第一个接受回调的。如果Activity没有对被点击的菜单项事件处理,那么事件被传递到Fragment。
处理Fragment的生命周期
管理Fragment的生命周期,有点像activity的生命周期。像activity一样,一个Fragment可以存在三种状态。
http://www.xilele.com/shequ/showarticle.jhtml?id=943836
http://www.xilele.com/shequ/showarticle.jhtml?id=943835
http://www.xilele.com/shequ/showarticle.jhtml?id=943834
http://www.xilele.com/shequ/showarticle.jhtml?id=943833
http://www.xilele.com/shequ/showarticle.jhtml?id=943832
http://www.xilele.com/shequ/showarticle.jhtml?id=943831
http://www.xilele.com/shequ/showarticle.jhtml?id=943830
http://www.xilele.com/shequ/showarticle.jhtml?id=943829
http://www.xilele.com/shequ/showarticle.jhtml?id=943828
http://www.xilele.com/shequ/showarticle.jhtml?id=943827
http://www.xilele.com/shequ/showarticle.jhtml?id=943826
http://www.xilele.com/shequ/showarticle.jhtml?id=943825
http://www.xilele.com/shequ/showarticle.jhtml?id=943824
http://www.xilele.com/shequ/showarticle.jhtml?id=943823
http://www.xilele.com/shequ/showarticle.jhtml?id=943822
http://www.xilele.com/shequ/showarticle.jhtml?id=943821
http://www.xilele.com/shequ/showarticle.jhtml?id=943820
http://www.xilele.com/shequ/showarticle.jhtml?id=943819
http://www.xilele.com/shequ/showarticle.jhtml?id=943818
http://www.xilele.com/shequ/showarticle.jhtml?id=943817
http://www.xilele.com/shequ/showarticle.jhtml?id=943816
http://www.xilele.com/shequ/showarticle.jhtml?id=943815
http://www.xilele.com/shequ/showarticle.jhtml?id=943814
http://www.xilele.com/shequ/showarticle.jhtml?id=943813
Resume 状态
Fragment在运行的activity中是可见的
Paused状态
另一个activity在最前面并且拥有焦点,但是这个Fragment所在的activity仍然是可见的。(前景activity是透明或者没有占满整个屏幕)
Stopped状态
Fragment是不可见的。不管主activity被停止了或者Fragment被从当前activity中移除并添加到返回栈中。被停止的Fragment仍然是活着的()。所有的状态和成员信息都被系统保存着。然而,如果主activity被销毁,Fragment将不再被用户可见,并且会被销毁。
像activity一样,可以使用Bundle保存Fragment的状态,以防止activity所在的进程被销毁,当activity被重新创建的时候,需要恢复Fragment的状态。可以通过Fragment的onSaveInstanceState()回调保存状态,在onCreate(),onCreateView()或者onActivityCreated()中恢复。
Activity与Fragment的最大不同点是它们如何被保存在各自的返回栈中。Activity被停止的时候,系统会将它放入返回栈中,然而一个Fragment被activity管理着,只有在移除Fragment的事务中明确通过调用addToBackStack()方法请求,Fragment才会被放入返回栈中。
其他情况下,管理一个Fragment的生命与管理activity的生命周期一样。
与activity生命周期的协调
Activity的生命周期影响Fragment的生命周期。例如,当activity接收到onPause()调用是,在这个activity中的Fragment也会接收到。
当Fragment与一个activity关联的时候调用(Activity对象在此处被传入)
创建Fragment视图层的时候调用
当activity的onCreate()方法返回时被调用
当视图层相关的Fragment被移除时调用
当Fragment与activity失去关联的时候调用
当activity到达Resume状态,就可以从activity添加、移除Fragment。但是,只有activity在Resume状态时,Fragment的生命周期才可以独立的变化。
当activity离开Resume状态,Fragment又要将其生命周期与activity关联