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

Android4.0-Fragment框架实现方式剖析(一)

分类: Android UI 2012-09-19 18:59 14880人阅读 评论(8) 收藏 举报

android

目录(?)[+]

  1. 什么是Fragment
  2. 为什么要使用Fragment
  3. 实现Fragment的时候为什么要有一个默认的构造函数
  4. Fragment的生命周期是怎样与Activity的生命周期整合的
    1. onInflate回调
    2. onAttach回调
    3. onCreate回调
    4. onCreateView回调
    5. onActivityCreated回调
    6. Fragment与Activity相同生命周期调用
    7. onDestroyView回调
    8. onDestroy回调
    9. onDetach回调
    10. 巧妙使用setRetainInstance
  5. 怎样管理Fragment

经过反复的学习对比,个人觉得带着问题学习新知是最有效的学习方式,因此文本就以提问的方式来讲述Fragment框架实现方式。

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的实例:

[java] view plaincopyprint?

  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. }
public static MyFragment newInstance(int index){
    MyFragment mf = new MyFragment();
    Bundle args = new Bundle();
    args.putInt("index",index);
    mf.setArguments(args);
    return mf;
}
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线程),在实际项目中触发后台线程进行准备非常有用,阻塞调用应该位于后台线程中。)

示例代码:

[java] view plaincopyprint?

  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. }
/**
     * During creation, if arguments have been supplied to the fragment
     * then parse those out.
     */
    @Override public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle args = getArguments();
        if (args != null) {
            mLabel = args.getCharSequence("label", mLabel);
        }
    }
4.onCreateView()回调

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

API文档:public ViewonCreateView (LayoutInflaterinflater,ViewGroupcontainer,BundlesavedInstanceState)--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父元素中,该关联会自动完成。如果在此回调中将碎片的视图层次结构附加到父元素,很可能会出现异常。实例代码如下所示:

[java] view plaincopyprint?

  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. }
 /**
     * Create the view for this fragment, using the arguments given to it.
     */
    @Override public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.hello_world, container, false);// 不能将Fragment的视图附加到此回调的容器元素,因此attachToRoot参数必须为false
        View tv = v.findViewById(R.id.text);
        ((TextView)tv).setText(mLabel != null ? mLabel : "(no label)");
        tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
        return v;
    }
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()方法来获取碎片管理器。代码如下所示:

[java] view plaincopyprint?

  1. FragmentManager fragmentManager = getFragmentManager()
  2. FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
时间: 2024-10-25 23:56:09

关于Fragment框架,说的够清晰了。。。的相关文章

android FragmentActivity+FragmentTabHost+Fragment框架布局

这周比较闲,计划系统的学习一下android开发,我本是一名IOS程序员,对手机开发还是有自己的一套思路的, 固这套思路用到我当前学android上了,先选择从Main页面的tabbar部分代码入手, Android框架布局方式大致分两种, TabActivity+TabHost+Activity,这种方式已过期, 另一种就是 FragmentActivity+FragmentTabHost+Fragment这种方式是当前最新的方式,也是google推荐使用方式,那么我当然选第二种方式了,于是就

你的日志组件记录够清晰嘛?--自己开发日志组件 Logger

现在现成的日志组件实在是太多太多,为什么我还需要自己实现呢????? 需求来源于java的log4j, [07-31 16:40:00:557:WARN : com.game.engine.thread.ServerThread:117] -> 全局排行榜同步执行器-->ServerThread[全局排行榜同步执行器]执行 执行时间过长:23 简单的一句日志信息,但是我却可以很清晰的定位日志输出的代码位置:com.game.engine.thread包下面的ServerThread这个类文件的

LitePal + Gson + Volley的ORM框架尝试方案

为了紧跟技术潮流,目前的项目开始采用ORM的思想进行重新设计. 数据库采用轻量级ORM框架LitePal,Json解析采用Gson,网络框架采用Volley. 如果只是单纯的将这些第三方框架引进来,事情就简单多了,但这样意义不大,所以我们就结合项目的需求探索这三者的结合方案. Volley的改造比较大,结合了OkHttp,在API方面采取了链式调用的方式,可以像这样写代码: Volley.url("").params("", "").done()

框架音频整理513

  框架音频整理: 1 .Strtus工作原理: (1)Strtus本身是一个mvc框架,对底层的servlet进行了封装.Struts的前端是一个核心控制器.叫做StrutsPrepareAndExecuteFilter, (2)这个核心控制器StrutsPrepareAndExecuteFilter是配置在web.xml文件中的,配置的所有请求都会通过web容器进入到strtus框架, (3)从前端发来的请求request,request进来之后会调用ActionProxy(控制器的代理类)

PHP框架的基本原理以及选择标准

PHP框架的原理 说到PHP框架,可能很多PHP新手会感到有些胆怯.其实,PHP框架也不是那么深不可测的,框架就是别人使用PHP基础只是为你写好了的东西,只是封装在一起:这就好比我们使用PHP的函数,函数都是已近写好了的,我们只要按照函数使用的规则,比如,为函数传递参数的个数.类型以及函数返回的值类型等.理解了PHP函数的这些使用规则,我们就可以大胆.放心的去使用了.同样道理,PHP框架也有自己的规则,比如项目文件夹的部署.控制器类的命名规则以及数据表的命名规则等.了解了PHP框架的使用规则我们

TinyAdmin前端展现框架

一直在苦苦寻找一个合适的前端框架,少说也看了几十个. ext太重,而且有内存泄露,在IE下就是个悲剧. dhtmlx,速度比较好,开源是GPL不适合企业应用,商业的要钱,倒也不贵万把块钱,但是样式比较接近于传统管理台应用,另外一个弊端是比较小众 Dojo,其实架构比较好,但是比较小众 Semantic:非常好的一个框架,但是成熟度不太好,对IE支持尤其比较差,另外比较小众 easyui:相对来说,也是一个不错的框架了,但是样式有点接近ext,也存在内存泄露 wijmo:非常完善的前端框架,但是比

【持久化框架】Mybatis简介与原理

从这篇博文开始我们学习一下Mybatis,希望大家提出宝贵的建议. 什么是Mybatis MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis .iBATIS一词来源于"internet"和"abatis"的组合,是一个基于Java的持久层框架.iBATIS提供的持久层框架包括SQL Maps和Data Access Objec

使用scrapy框架---爬小说,入库

本人步骤: 1>setting.py: BOT_NAME = 'newding' SPIDER_MODULES = ['newding.spiders']NEWSPIDER_MODULE = 'newding.spiders' ROBOTSTXT_OBEY = True ITEM_PIPELINES = { 'newding.pipelines.NewdingPipeline': 300,} 以上配置:创建项目会自动出现这些 以下是想要入数据库的(阶段): MYSQL_USER = 'root'

Android4.0-Fragment框架实现方式剖析(一)

目录(?)[+] 经过反复的学习对比,个人觉得带着问题学习新知是最有效的学习方式,因此文本就以提问的方式来讲述Fragment框架实现方式. 1.什么是Fragment? Fragment包含在Activity中,Fragment只能存在于Activity的上下文(context)内,没有Activity就无法使用Fragment,因此Fragment只能在Activity的上下文(context)创建.Fragment可以作为Activity的一部分,Fragment和Activity非常相似