1、什么是Fragment?

Fragment包含在Activity中,Fragment只能存在于Activity的上下文(context)内,没有Activity就无 法使用Fragment,因此Fragment只能在Activity的上下文(context)创建。Fragment可以作为Activity的一部 分,Fragment和Activity非常相似,Fragment拥有一个与她相关的视图层次结构,拥有一个与活动非常相似的生命周期。

2、为什么要使用Fragment?

Activity的使用局限:不能将多个Activity活动界面放在屏幕上一并显示。因此创建了Fragment来弥补Activity的局限。Fragment可以像Activity一样响应Back键等类似Activity的功能。

3、实现Fragment的时候,为什么要有一个默认的构造函数?

谈到这儿,就不得不说一下Fragment的结构。Fragment的结构包括:视图层次结构、初始化参数的包

首先来看一下Fragment和Activity的继承关系,如图1-1,1-2所示:

          

图1-1 Fragment类继承关系                                                                                                             图1-2 Activity类继承关系

从图中很容易看出Fragment是直接从Object继承的,而Activity是Context的子类。因此我们可以得出结论:Fragment不是Activity的扩展。但是与Activity一样,在我们使用Fragment的时候我们总会扩展Fragment(或者是她的子类),并可以通过子类更改她的行为。

Fragment可以拥有一个与用户交互的视图层次结构,该视图层次结构和Activity的视图层次结构一样,也可以通过XML布局规范创建 (扩充)或者代码创建。(备注:如果视图层次结构需要向用户显示,则必须将Fragment的视图层次结构附加到Activity的试图层次结构中)

前面介绍了那么多,只是为了抛砖引玉,接下来进入正题,为什么Fragment必须包含一个默认的构造函数(在Java类 中,如果没有构造函数,在编译的时候会自动创建一个不带参数的默认构造函数。因此如果Java类中没有其他的构造函数,可以将默认函数省略,编译器会自动 创建;如果Java类中有其他构造函数时,在编译时系统不会创建默认的构造函数,因此在有非默认构造函数时,又需要在编译时出现默认构造函数,就必须在 Java类中显示的写出默认构造函数)初始化参数的包——类似于活动,碎片可由系统自动保存并在以后还原。当系统还原Fragment时,她调用默认的构 造函数,然后将参数包还原到新建的Fragment。该Fragment执行的后续回调能够访问这些参数,可以将碎片还原到上一个状态。因此在使用 Fragment时,一定要确保以下两点:

  1. 确保Fragment类存在默认的构造函数;
  2. 在Fragment创建后立即添加一个参数包(Bundle),使Fragment重建时可以正确设置Fragment,也使Android系统可以在必要时正确还原Fragment。

小结:Fragment的子类必须具有默认的构造函数和一个参数包,因为Fragment在重新创建的时候会调用默认的构造函数,而且会在重新创建时将状态保存到一个包(Bundle)对象(备注:注意区分对象包和前面所说的参数包) 中,这个包(Bundle)对象会被回送到该Fragment的onCreate()回调。这个保存的包(Bundle)也会传递到 onInflate()\onCreateView()\onActivityCreated()。(备注:这不是作为初始化参数而附加的包。可能在这个 包中存储Fragment的当前状态,而不是应该用于初始化她的值)

4、Fragment的生命周期是怎样与Activity的生命周期整合的?

在使用Fragment之前,一定要了解Fragment的生命周期。Fragment的生命周期相比Activity的生命周期要更为复杂,理 解何时处理Fragment至关重要。由于Fragment是依赖于Activity的,接下来看一下两者的生命周期有什么异同,Fragment与 Activity生命周期如图1-3、1-4所示:

           

图1-3 Activity运行时Fragment生命周期                                                                                   图1-4 Activity生命周期

通过对Fragment和Activity对比,将会发现许多不同之处,主要原因是因为Activity和Fragment之间需要交互。 相信大家对Activity的生命周期已经非常熟悉,在此就不做过多介绍,直接切入正题介绍Fragment的生命周期。在Fragment开始阶段,Fragment会以对象的形式存在于内存中。创建Fragment实例有如下两种情况:

  1. 创建Fragment实例;
  2. 系统从保存状态重新创建Fragment的情况下,将初始化参数添加到碎片对象中。当系统从保存的状态还原Fragment时,会调用默认的构造函数,然后附件初始化参数包;

使用代码创建Fragment的实例:

  1. public static MyFragment newInstance(int index){
  2. MyFragment mf = new MyFragment();
  3. Bundle args = new Bundle();
  4. args.putInt("index",index);
  5. mf.setArguments(args);
  6. return mf;
  7. }
1.onInflate()回调

API文档:onInflate(Activity activity, AttributeSet attrs, Bundle savedInstanceState)--Called when a fragment is being created as part of a view layout inflation, typically from setting the content view of an activity.

Called when a fragment is being created as part of a view layout inflation, typically from setting the content view of an activity. This may be called immediately after the fragment is created from a tag in a layout file. Note this is before the fragment's onAttach(Activity) has been called; all you should do here is parse the attributes and save them away.

如果Fragment是由<fragment>标记定义的(通常是在活动调用setContentView()来设置自己的主要布 局),Fragment将调用自己的onInflate()回调。这一过程中传入一个AttributeSet(包含来 自<fragment>标记的特性)和一个保存的包。如果重新创建碎片,并且之前在onSaveInstanceState()中保存了某种 状态,此包(Bundle)将包含保存的状态值。onInflate()预计开发人员会读取特性值并保存她们供以后使用。在Fragment的 onInflate()这一生命阶段,对用户界面执行任何操作都尚早,因为Fragment甚至还没有与其Activity关联。

(备注:onInflate()文档与实际使用不符,文档表明onInflate()始终在onAttach()之前调用。实际上,在 Activity重新启动后,onInflate()可能在onCreateView()之后调用。这对于将值设置到包(Bundle)中并调用 setArguments()而言太迟了,Android官方文档中Fragment生命周期的图示中也没有将onInflate()包含在生命周期,看来Android的大牛们也很难预测onInflate()会在何时回调)

2.onAttach()回调

好了,讨论了那么纠结的问题,相比大家都被我绕晕了吧,但是追求技术的道路中是不能有半点怠慢的,透彻分析问题才是我们追求技术的使命。如果大家都 头有点晕的话,建议大家先看部喜剧片吧,清醒一下头脑!如果还能坚持的,就跟随我的思路继续探寻。onAttach()回调,回头一看,MyGod,该方 法终于出现在Fragment生命周期的图解中了,总算是引领大家步入正轨了。

API文档:public void onAttach (Activity activity) --Called when a fragment is first attached to its activity. onCreate(Bundle)will be called after this.

onAttach()回调将在Fragment与其Activity关联之后调用。需要使用Activity的引用或者使用Activity作为其他操作的上下文,将在此回调方法中实现。

注意:Fragment类有一个getActivity()方法,返回与Fragment关联的Activity。在Fragment的整个生命周期中,初始化参数包(Bundle)可以从碎片的getArguments()方法获得。

切忌:将Fragment附加到Activity以后,就无法再次调用setArguments()——除了在最开始,无法向初始化参数添加内容。

3.onCreate()回调

接下来回调的方法就是onCreate(),大家可以不要与Activity的onCreate()回调方法混淆了。此回调获取传入的参数包(备 注:如果在创建时设置了参数包(Bundle)的话就可以获得),不应该将需要依赖于Activity视图层次结构的存在性的代码放在此回调方法中,尽管 Fragment现在可能已经与其Activity关联,但是我们还没有获得Activity的onCreate()已完成的通知,所以不能将依赖于 Activity视图层次结构存在性的代码放入此回调方法中。

(备注:在onCreate()回调方法中,我们应该尽量避免耗时操作(避免阻塞UI线程),在实际项目中触发后台线程进行准备非常有用,阻塞调用应该位于后台线程中。)

示例代码:

  1. /**
  2. * During creation, if arguments have been supplied to the fragment
  3. * then parse those out.
  4. */
  5. @Override public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. Bundle args = getArguments();
  8. if (args != null) {
  9. mLabel = args.getCharSequence("label", mLabel);
  10. }
  11. }
4.onCreateView()回调

接下来的回调方法是onCreateView(),在该回调方法中应该返回该Fragment的一个视图层次结构。

ViewonCreateView (LayoutInflaterinflater,ViewGroupContainerBundlesavedInstanceState)--Called to have the fragment instantiate its user interface view. This is optional, and non-graphical fragments can return null (which is the default implementation). This will be called betweenonCreate(Bundle)andonActivityCreated(Bundle).

其中的Bundle为状态包(备注:必须和前面所说的参数包(Bundle)区分开来)注意:不要将视图层次结构附加到传入的ViewGroup父元素中,该关联会自动完成。如果在此回调中将碎片的视图层次结构附加到父元素,很可能会出现异常。实例代码如下所示:

  1. /**
  2. * Create the view for this fragment, using the arguments given to it.
  3. */
  4. @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
  5. Bundle savedInstanceState) {
  6. View v = inflater.inflate(R.layout.hello_world, container, false);// 不能将Fragment的视图附加到此回调的容器元素,因此attachToRoot参数必须为false
  7. View tv = v.findViewById(R.id.text);
  8. ((TextView)tv).setText(mLabel != null ? mLabel : "(no label)");
  9. tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
  10. return v;
  11. }
5.onActivityCreated()回调

终于到了与用户交互的时刻了,onActivityCreated()回调会在 Activity完成其onCreate()回调之后调用。在调用onActivityCreated()之前,Activity的视图层次结构已经准备 好了,这是在用户看到用户界面之前你可对用户界面执行的最后调整的地方。(备注:如果Activity和她的Fragment是从保存的状态重新创建的, 此回调尤其重要,也可以在这里确保此Activity的其他所有Fragment已经附加到该活动中了)

6. Fragment与Activity相同生命周期调用

接下来的onStart()\onResume()\onPause()\onStop()回调方法将和Activity的回调方法进行绑定,也就是说与Activity中对应的生命周期相同,因此不做过多介绍。

7.onDestroyView()回调

该回调方法在视图层次结构与Fragment分离之后调用。

8.onDestroy()回调
不再使用Fragment时调用。(备注:Fragment仍然附加到Activity并任然可以找到,但是不能执行其他操作)
9.onDetach()回调
Fragme生命周期最后回调函数,调用后,Fragment不再与Activity绑定,释放资源。
通过以上说明,纵观Fragment生命周期和Activity生命周期整合后如图1-5所示:
                          
                 图1-5 Activity和Fragment生命周期整 合                                                                                        图1-6 Fragment生命周期
10.巧妙使用setRetainInstance()

为什么会在这儿花一定的篇幅详细说明setRetainInstance()方法呢?因为此方法可以有效地提高系统的运行效率,对流畅性要求较高的应用可以适当采用此方法进行设置。

Fragment有一个非常强大的功能——就是可以在Activity重新创建时可以不完全销毁Fragment,以便Fragment可以恢 复。在onCreate()方法中调用setRetainInstance(true/false)方法是最佳位置。当Fragment恢复时的生命周期 如图1-6所示,注意图中的红色箭头。当在onCreate()方法中调用了setRetainInstance(true)后,Fragment恢复时 会跳过onCreate()和onDestroy()方法,因此不能在onCreate()中放置一些初始化逻辑,切忌!

5、怎样管理Fragment?

既然Fragment必须存在Activity的上下文(context) 内,那么怎样管理Fragment是我们接下来需要关心的话题。FragmentManager组件负责管理属于一个活动的碎片(包括后退栈上的碎片和空 闲的碎片)。可以在Activity或附加的Fragment上使用getFragmentManager()方法来获取碎片管理器。代码如下所示:

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

Android4.0-Fragment框架实现方式剖析(一)的更多相关文章

  1. 【Android 界面效果40】Android4.0-Fragment框架实现方式剖析(一)

    经过反复的学习对比,个人觉得带着问题学习新知是最有效的学习方式,因此文本就以提问的方式来讲述Fragment框架实现方式. 1.什么是Fragment? Fragment包含在Activity中,Fr ...

  2. 在Android4.0中Contacts拨号盘界面剖析(源码)

      通过在 ViewPager 的适配器对象中,发现过一下三行代码 private DialpadFragment mDialpadFragment; private CallLogFragment ...

  3. 关于Fragment框架,说的够清晰了。。。

    Android4.0-Fragment框架实现方式剖析(一) 分类: Android UI 2012-09-19 18:59 14880人阅读 评论(8) 收藏 举报 android   目录(?)[ ...

  4. Android常用控件之Fragment仿Android4.0设置界面

    Fragment是Android3.0新增的概念,是碎片的意思,它和Activity很相像,用来在一个Activity中描述一些行为或部分用户界面:使用多个Fragment可以在一个单独的Activi ...

  5. 从壹开始前后端分离【 .NET Core2.0 +Vue2.0 】框架之十二 || 三种跨域方式比较,DTOs(数据传输对象)初探

    更新反馈 1.博友@落幕残情童鞋说到了,Nginx反向代理实现跨域,因为我目前还没有使用到,给忽略了,这次记录下,为下次补充.此坑已填 2.提示:跨域的姊妹篇——<三十三║ ⅖ 种方法实现完美跨 ...

  6. 精通Android4.0开发视频【张泽华】-完整版下载

    观看须知: 本视频教程为黑马程序员 张泽华老师历经2年时间整理 适合有JavaWeb基础同学学习,教程采用的AVI方式发布,所以看起来很流畅. 视频概括: 1. 本套视频不同于市面上任何一套andro ...

  7. android FragmentActivity+FragmentTabHost+Fragment框架布局

    这周比较闲,计划系统的学习一下android开发,我本是一名IOS程序员,对手机开发还是有自己的一套思路的, 固这套思路用到我当前学android上了,先选择从Main页面的tabbar部分代码入手, ...

  8. 关于解决android4.0系统中菜单无法添加Icon的问题

    在Android4.0系统中,创建菜单Menu,然后通过setIcon方法给菜单添加图标是无效的,图标不会显出来,而之前的系统中是可以显示出来的.这个问题的根本原因在于4.0系统中,涉及到菜单的源码类 ...

  9. 浅谈android4.0开发之GridLayout布局

    作者:李响 本文重点讲述了自android4.0版本号后新增的GridLayout网格布局的一些基本内容,并在此基础上实现了一个简单的计算器布局框架.通过本文,您可以了解到一些android UI开发 ...

随机推荐

  1. VS 2015打开项目闪退,新建项目提示未将对象引用到实例

    因为开发需要,要把开发工具换成visual studio2015,装完之后会有警告“js”安装的问题,打开VS也没有问题, 但是一打开项目就闪退,新建项目也不行,查看应用程序日志,报错提示如下: .N ...

  2. Sharepoint学习笔记—习题系列--70-576习题解析 -(Q32-Q35)

    Question 32 You are designing the modification of an existing SharePoint 2010 intranet site for a sc ...

  3. Android Content Provider Guides

    Android Content Provider Guides Content Providers管理对结构化数据集的访问.它们包装数据,并且提供一种定义数据安全的机制. Content provid ...

  4. 如何自定义ViewGroup

    依照惯例,先从一个例子说起. 很简单,3张扑克牌叠在一起显示.这个布局效果该如何实现呢?有的同学该说了,这很简单啊,用RelativeLayout或FrameLayout,然后为每一个扑克牌设置mar ...

  5. Xcode cannot launch because the device is locked.

    When you plug in your iPhone, it will ask you to trust the computer. If you already trust and unlock ...

  6. IOS开发之代理的设计小技巧

    1.关于代理对象的设计小技巧 在设计一个类,需要通过代理和协议来从外部获取需要的动态的数据.那么在这里设计使用代理会有两种方法. <第一种方法> 也是比较常见的: 在你设计的类中,声明一个 ...

  7. 解析ABP框架中的事务处理和工作单元,ABP事务处理

    通用连接和事务管理方法连接和事务管理是使用数据库的应用程序最重要的概念之一.当你开启一个数据库连接,什么时候开始事务,如何释放连接...诸如此类的. 正如大家都知道的,.Net使用连接池(connec ...

  8. 预写式日志(Write-Ahead Logging (WAL))

    SQL Server中使用了WAL(Write-Ahead Logging)技术来保证事务日志的ACID特性.而且大大减少了IO操作. WAL的核心思想是:在数据写入到数据库之前,先写入到日志.再将日 ...

  9. 利用 druid 解析器解析SQL

    最近参与一个开源项目,一个功能的实现,用到了 druid 解析器来解析SQL,记录下如果使用 druid 来解析SQL,实现对SQL的拦截改写. 1. 对 insert 语句进行解析: private ...

  10. C#调用自定义表类型参数

    -SQL SERVER生成测试环境: --创建测试DB CREATE database Sales; go USE Sales GO --创建表类型 IF TYPE_ID('LocalDT') IS ...