Fragments

来源:互联网 发布:北京 软件 培训 编辑:程序博客网 时间:2024/05/22 14:36

  (一)Fragments要点:

a) Fragment表现为一个Activity用户界面的一部分;

b) 你可以合并多个fragment在一个单独的activity中;

c) 也可以在多个activity中重复使用一个Fragment;

d) Fragment有自己的生命周期;

e) 接收自己的输入事件;

f) 在activity运行的过程中,你可以添加或移除一个Fragment;

g) 一个Fragment必须嵌入在一个Activity中使用,Fragment的生命周期由其宿主Activity控制

例如:一个Activity A,含有两个Fragment(F1、F2),当A进入pause()时,F1、F2也都会进入pause()状态;而当A进入destroy状态时,F1、F2也都会进入destroy状态。      但是,当A正在运行时(处于resume状态),可以单独的管理每一个Fragment(F1、F2),比如添加F1或者删除F2。

h) Activity在管理Fragment时是通过FragmentManager和FragmentTransaction进行的,对Fragment的add()、remove()和replace()的操作都需要在一个Transaction中管理;

i) 当对Fragment进行操作时,可以把这些操作放在一个由Activity维护的后台堆栈中(Stack),Stack中的每一个元素都是对Fragment进行操作的事务记录;当用户按下回退键(Back)时,可以从Stack中回溯一个事务;

j) Fragment可以作为布局(Layout)的一部分添加到Activity中,也可以作为一个没有视图界面的单纯的工作器(Worker)添加到Activity中。     

当fragment作为Layout添加时,Fragment就相当于Activity的总体视图系统的一部分,它应当被添加到某一个ViewGroup中。可以使用<fragment>标签在Activity的layout文件中添加,也可以用编码方式添加


(二)设计哲学         

 在3.0中引入的Fragment的概念,主要是为了能够在大尺寸的屏幕上(如Tablet)支持更加灵活的UI设计。在大尺寸屏幕上有更多的空间可以放置更多的视图控件,这也意味着应用程序可能要处理更多的用户交互。          

没有Fragment的概念之前,Activity会负责整个的布局管理,如果用户交互逻辑很多,Activity就会非常臃肿且难以维护。而Fragment的主要作用就是帮Activity分担这些布局管理工作,把对布局、视图的操作和一些业务逻辑分拆到多个Fragment中。          总之,Activity(Controller)和Layout(View)之间加入Fragment的概念的好处,首先是将Acitivty对用户交互的逻辑模块化,其次是可以非常灵活的重用这些Fragment


例如上图,左侧是一个邮件列表,右侧是一个邮件内容查看界面。

1.以前,在手机或者Tablet屏幕上,会将列表布局(LayoutA)放在一个ActivityA中处理,但用户点击一封邮件条目时,屏幕会跳转到另一个邮件内容查看界面(LayoutB),而LayoutB是放在一个ActivityB中处理的;

2.现在,在Tablet屏幕上,有了Fragment的概念以后,我们可以将LayoutA放在一个FragmentA中处理,将LayoutB放在FragmentB中处理,同时将FragmentA和FragmentB统一交给ActivityA维护,就不需要ActivityB了;

3. Fragment的概念并不是只适用于大尺寸屏幕,它也可以自适应到小尺寸的屏幕上。比如使用了Fragment的概念针对Tablet 编写了一套邮件客户端,后来又希望将这个客户端放到一个手机上运行,此时不需要更改布局代码,应用本身会自动的将FragmentA和FragmentB分开显示,既先显示邮件列表界面,当选中某封邮件时会跳转到另一个界面显示邮件详细内容


  (三)创建Fragment      

创建Fragment和创建Activity类似,只需要继承Fragment类(或其子类)并实现一些必要的生命周期回调方法。            应当实现的生命周期方法有:    

onAttach() 在fragment关联到activity被调用一次

onCreate()  当Fragment被创建时会被调用;      

onCreateView() 第一次绘制界面时会被调用,有界面的Fragment,返回它的Layout的根视图;没有界面的,返回null; 

onActivityCreate()告诉fragment宿主activity已经完成了自身的Activity.onCreate()

onViewStateRestored()告知fragment所有的view布局状态已经被存储

onStart() 使得fragment对于用户是visible的

onResume() 使得fragment对于用户是可以交互的

     

onPause()fragment不在和用户交互了,原因是对fragment做了操作或者所在activity被paused

onStop() fragment不可见了。原因原因是对fragment做了操作或者所在activity被stoped

onDestroyView()让fragment清除关联View的资源

onDestroy()destory fragment

onDetach() 立即断开和宿主activity的关联关系


1,添加用户交互接口

一般情况下fragment都会提供一个layout,这就需要实现onCreateView()方法。例如:

[java] view plaincopy
  1. public static class ExampleFragment extends Fragment {  
  2.     @Override  
  3.     public View onCreateView(LayoutInflater inflater, ViewGroup container,  
  4.                              Bundle savedInstanceState) {  
  5.         // Inflate the layout for this fragment  
  6.         return inflater.inflate(R.layout.example_fragment, container, false);  
  7.     }  
  8. }  

参数container 就是fragment所在layout。

inflate方法第三个参数false是因为这个fragment已经在container的layout中的了。如果为true就会在layout中生成冗余的view。

2. 添加Fragment到Activity中      

如之前所说的,有两种方法将Fragment添加到Activity中。

1.1.在Activity 的layout文件中添加Fragment使用这种方式,可以把Fragment理解为一个普通的View,可以制定它的布局属性,

[java] view plaincopy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:orientation="horizontal"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent">  
  6.     <fragment android:name="com.example.news.ArticleListFragment"  
  7.             android:id="@+id/list"  
  8.             android:layout_weight="1"  
  9.             android:layout_width="0dp"  
  10.             android:layout_height="match_parent" />  
  11.     <fragment android:name="com.example.news.ArticleReaderFragment"  
  12.             android:id="@+id/viewer"  
  13.             android:layout_weight="2"  
  14.             android:layout_width="0dp"  
  15.             android:layout_height="match_parent" />  
  16. </LinearLayout>  

注意<fragment>“标签的android:name”属性的值是你自定义的Fragment类的全路径名

当系统创建Activity的布局时,会初始化每一个Fragment,调用它们的onCreateView(...)方法获取Layout,并用Layout替换掉相应的<fragment>标签。   

完全当做view来看待,可以通过show,hide的逻辑来操作一个layout下的多层fragment,这个适用于简单的逻辑。        


注意:每一个Fragment都需要有一个唯一的标识符(ID),如果Activity进入restart状态,系统可以根据这个ID 重新载入对应的Fragment;另外,你也可以用这个ID 对相应的Fragment做一些事务性操作,如remove()。有三种方式可以为一个Fragment设 置ID:

a,在<fragment>“标签中设置android:id”属性,提供一个唯一的ID;

b,在<fragment>“标签中设置android:tag”属性,提供一个唯一的字符串;

c,如果以上两种属性都没有设置,系统会使用这个Fragment的父容器的ID。



1.2.用编码的方式添加Fragment到一个已存在ViewGroup中           

在activity运行的任何时刻,都可以将一个Fragment添加到Activity的Layout中,只需要简单的提供一个ViewGroup来放置这个Fragment即可。

具体要对Fragment进行操作时,需要在Activity中使用FragmentTransaction,以事务的方式对Fragment进行add、remove和replace。           

用以下的方式可以从Activity中获取一个FragmentTransaction实例:

[java] view plaincopy
  1. FragmentManager fragmentManager =getFragmentManager();  
  2. FragmentTransaction fragmentTransaction =fragmentManager.beginTransaction();  
然后可以用add()方法添加一个Fragment,需要指定要添加哪个Fragment和要添加到哪个ViewGroup中
[java] view plaincopy
  1. ExampleFragment fragment = new ExampleFragment();  
  2. fragmentTransaction.add(R.id.fragment_container,fragment);  
  3. fragmentTransaction.commit();  
  add()的第一个参数是fragment要放入的ViewGroup, 由resource ID指定,第二个参数是需要添加的fragment.一旦用FragmentTransaction做了改变,为了使改变生效,必须调用commit().


3. 添加一个无UI的Fragment可以使用add(Fragment, String)方法向Activity添加一个Fragment实例。

第一个参数是要添加的Fragment实例,因为是无UI的(即这个Fragment实例不会和Activity的Layout中的任何一个View有关联),所以系统不会去调用这个Fragment实例的onCreateView(...)方法,我们也不需要去实现这个方法。第二个参数是该Fragment实例的唯一标识符(不是一个资源ID),同时也是能够标识该实例的唯一途径(无法为其设置资源ID)。例如希望在Activity运行过程中获得该Fragment的引用,可以使用findFragmentByTag(String tag)方法。

无UI的Fragment的代码示例:FragmentRetainInstance.java



(四)管理Fragment

要在Activity中管理Fragment,需要使用FragmentManager,通过调用Activity的方法getFragmentManager()获得FramentManager的对象。你可以使用FragmentManager做这样一些事情:

1.1.获取在Activity中存在的某个Fragment:getFragmentById(int id) – 有UI的Fragment

getFragmentByTag(String tag) – 有UI或无UI的Fragment都可以

1.2.从后台的BackStack中弹出存放的Fragment:popBackStack(...)

1.3.为后台的BackStack添加监听器,监听其任何改动:addOnBackStackChangedListener(...)

1.4.像之前提到过的,可以使用FragmentManager打开一个事务:FragmentTransaction beginTransaction();



(五)执行Fragment事务

使用Fragment的一个非常好的特性,是为了能够和用户交互,可以灵活的add、remove、replace(或其他操作)一个Fragment。你向Activity提交的任何对Fragment的修改都被作为一个事务(Transaction)。可以将每一个Transaction都保存在一个BackStack中(由Activity维护),这样用户就可以使用BACK键回退之前每一次对Fragment的修改了,就像是从一个Activity回退到以前的一个Activity一样。

  每一个事务都是同时要执行的一套变化.可以在一个给定的事务中设置你想执行的所有变化,使用诸如 add()、remove()和 replace().然后, 要给activity应用事务, 必须调用 commit().
在调用commit()之前, 你可能想调用 addToBackStack(),将事务添加到一个fragment事务的backstack. 这个back stack由activity管理, 并允许用户通过按下 BACK按键返回到前一个fragment状态.

  举个例子, 这里是如何将一个fragment替换为另一个, 并在后台堆栈中保留之前的状态:

[java] view plaincopy
  1. // Create new fragment and transaction  
  2. Fragment newFragment = newExampleFragment();  
  3. FragmentTransaction transaction =getFragmentManager().beginTransaction();  
  4. // Replace whatever is in thefragment_container view with this fragment,  
  5. // and add the transaction to the backstack  
  6. transaction.replace(R.id.fragment_container,newFragment);  
  7. transaction.addToBackStack(null);  
  8. // Commit the transaction  
  9. transaction.commit();  
注意:       

 一次更改的所有的操作都包含在一个FragmentTransaction实例中;

BackStack中存放的是FragmentTransaction实例;

commit()操作必须在整个事务的最后执行,其他操作的顺序没有要求(addToBackStack()的调用顺序在文档中没有特别说明,可以认为是只要在commit()之前即可)。

addToBackStack()的作用:假设在一个FragmentTransaction中remove了一个Fragment,如果没有调用addToBackStack(),那么系统会destroy掉这个Fragment,如果调用了addToBackStack(),系统仅仅会stop这个Fragment而不会destroy掉,当用户回退的remove操作的时候,系统会resume这个Fragment。

提示:对于每一个FragmentTransaction,都可以应用一个转换动画(transition animation),使用方法是在commit()之前调用setTransition()方法。

事务在commit()之后,并不会立即执行而是会被安排在UI线程中尽可能快的执行(应该放在了MessageQueue中)。如果确实有必要,例如在另一个线程中的一项任务要依赖于UI线程中的一个事务执行完毕,那么可以在UI线程中调用executePendingTransactions()去立即执行该事务,除此之外通常没有必要这样做

注意:你只能在一个Activity保存它的状态(onPause、onStop)之前执行commit(),否则的话将会抛出异常,这是因为如果这个Activity需要restore的话,那commit()之后的状态会丢失(不明白,再调查)。如果某些情况下允许丢失commit,可以调用commitAllowingStateLoss()


(六)与Activity通信

尽管Fragment是独立于Activity的,并且可以被多个Activity使用,但是一个给定的Fragment实例是直接与包含它的Activity绑定的。在Fragment中可以使用getActivity()方法访问其宿主Activity,例如:

[java] view plaincopy
  1. View listView =getActivity().findViewById(R.id.list);  
同样地,activity可以通过从FragmentManager获得一个到Fragment的引用来调用fragment中的方法, 使用findFragmentById() 或 findFragmentByTag().
[java] view plaincopy
  1. ExampleFragment fragment =(ExampleFragment) getFragmentManager().findFragmentById(R.id.example_fragment);  


1. 向Activity创建事件回调

有时我们希望能够将在Fragment中获得到的事件共享到Activity中,一种非常好的方式是在Fragment中定义一个回调接口,同时需要宿主Activity实现这个接口。这样,当宿主Activity中接口中获得回调事件时,就可以将事件信息共享到它维护的其他的Fragment中。

举例说明,一个邮件应用,有两个Fragment:

ActivityA – 邮件主界面

FragmentA – 邮件列表界面

FragmentB – 邮件详细信息界面

当用户点击FragmentA列表中的一个邮件条目时,FragmentA应该把这个点击事件传递给ActivityA,这样ActivityA就可以通知FragmentB去显示相应的邮件内容。以下的代码示例中,FragmentA中定义了onArticleSelectedListener接口:

[java] view plaincopy
  1. public static class FragmentA extends ListFragment {  
  2.     ...  
  3.     // Container Activity must implement this interface  
  4.     public interface OnArticleSelectedListener {  
  5.         public void onArticleSelected(Uri articleUri);  
  6.    
  7.     }  
  8.     ...  
  9. }  
ActivityA应该实现这个接口。为了能够让FragmentA确保ActivityA实现了该接口,可以在FragmentA的onAttach()方法中做一下检查
[java] view plaincopy
  1. public static class FragmentA extends ListFragment {  
  2.     OnArticleSelectedListener mListener;  
  3.     ...  
  4.     @Override  
  5.     public void onAttach(Activity activity) {  
  6.         super.onAttach(activity);  
  7.         try {  
  8.             mListener = (OnArticleSelectedListener) activity;  
  9.          } catch (ClassCastException e) {  
  10.             throw new ClassCastException(activity.toString() + " must implementOnArticleSelectedListener");  
  11.         }  
  12.     }  
  13.    
  14.     ...  
  15.    
如果上述代码没有异常,mListener就会获得ActivityA的引用。FragmentA是一个ListFragment,用户点击一个List Item时,系统会调用FragmentA中的onListItemClick()方法,我们可以在这个方法里调用onArticleSelected()来传递点击事件
[java] view plaincopy
  1. public static class FragmentA extends ListFragment {  
  2.     OnArticleSelectedListener mListener;  
  3.     ...  
  4.     @Override  
  5.     public void onListItemClick(ListView l, View v, int position, long id) {  
  6.         // Append the clicked item's row ID with the content provider Uri  
  7.          Uri noteUri =ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);  
  8.         // Send the event and Uri to the host activity  
  9.        mListener.onArticleSelected(noteUri);  
  10.     }  
  11.    
  12.     ...  
  13.    
  14. }  


2. 向ActionBar中添加项目

Fragment可以向其宿主Activity的选项菜单(Options Menu)中添加项目(因此也可以向ActionBar中),方法:•在Fragment中重写onCreateOptionsMenu()方法;•在onCreate()方法中调用setHasOptionsMenu(),否则系统不会调用Fragment的onCreateOptionsMenu()方法;Fragment也可以使用registerForContextMenu()来注册一个上下文菜单(Context Menu),当用户要打开一个Context Menu时,会调用onCreateContextMenu(),当用户选择一个条目时,会调用onContextItemSelected()。

注意:尽管Fragment会接收到用户选择菜单选项的事件,但这个事件首先会被Activity获取,如果Activity没有处理这个事件的话,才会把它传给Fragment。

 这个规则适用于选项菜单和环境菜单.



(七)处理Fragment的生命周期

Fragment的生命周期和Activity很像,它可以存在于三种状态下:Resumed、Paused、Stopped。

像Activity一样,Fragment也可以使用Bundle,当Activity进程被kill掉的时候,为了能够在Activity被重新创建时恢复Fragment之前的状态,就需要使用onSaveInstanceState()保存Fragment被kill之前的状态,当Fragment被重新创建时,之前保存数据的Bundle就会被传递到Fragment的onCreate()、onCreateView()、和onActivityCreated()方法中。

Fragment的生命周期和Activity相比,最大的区别在于他们是如何被保存在后台堆栈中的。Activity是被stop时,默认的被保存在Task中,Task是一个Activity的堆栈,由系统维护;而Fragment是在被remove时,需要显示指定addToBackStack(),才会被压入由Activity维护的一个事务堆栈。此外,管理Fragment的生命周期与Activity是非常相似的。还需要掌握的一个知识点是Activity的生命周期是如何影响Fragment的生命周期的。如右图:



1. 协调与Activity的生命周期

Activity的生命周期直接影响了它所包含的Fragment的生命周期,当Activity进入到某一种状态时会调用Fragment中相应的状态的方法(如右图所示)。

Fragment还有一些额外的状态方法:onAttach() – 当Fragment被关联到Activity中时;

onCreateView() – 当创建与Fragment关联的视图结构时;

onActivityCreated() – 当activity的onCreate()方法返回时;

onDestroyView() – 当移除与Fragment关联的视图结构时;

onDetach() – 当从Activity中移除Fragment时;


一旦Activity进入resumed状态后,你就可以自由的添加或移除Fragment了。但是当Activity离开resumed状态后,Fragment又会被Activity的生命周期所影响


对于低版本:

Fragment是Android 3.0中引入的新功能,它的引入对于在平板电脑运行的应用很重要。尽管Android已经推出了4.2,可是在市场上仍然有相当数量的设备运行在Android3.0以下的版本,包括一些平板电脑。为了能让低版本的Android设备使用Fragment功能,Google推出了Android支持包。在引入了这个包之后,即使是基于Android 1.6 SDK的应用也可以使用Fragment,把屏幕分成多个部分。 
使用Android支持包并且尝试用Fragment代替Activity作为UI的基本构件有两个方面的好处:首先它可以统一Pad应用和手机应用的代码;其次,在引入了支持包之后,基于不同版本SDK的应用可以统一代码。这么做提高了代码的一致性,也降低了应用维护和升级的工作量。 
支持包包括多个支持库,每个都有不同的最低API等级,等级包括v4、v7、v13。例如,v4表示此支持包需要的最低Android API的等级是4,也就是Android1.6。v7则是v4的超集,包含v4的全部功能,它需要的最低API等级是7(Android 2.1)。



总结一下,

Fragment是一个“子Activity”或”面板“

能向用户显示数据和功能,与Activity相似

有它关联的视图结构,有生命周期,有返回栈

和Activity紧密联系在一起,包含在Activity中

创建Fragment

继承Fragment类实现生命周期方法,并组合到Activity

为管理Activity中的Fragment会用到FragmentManager与Fragment交互

Fragment操作

在保存状态和还原状态上与Activity 不同,操作简单

创建一个Fragment事物来执行添加,删除等操作


原创粉丝点击