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

通过反复的学习对比,我的以为带着问题学习新知是最有效的学习方式,所以文本就以提问的方式来说述Fragment框架实现方式。html

一、什么是Fragment?

Fragment包含在Activity中,Fragment只能存在于Activity的上下文(context)内,没有Activity就没法使用Fragment,所以Fragment只能在Activity的上下文(context)建立。Fragment能够做为Activity的一部分,Fragment和Activity很是类似,Fragment拥有一个与她相关的视图层次结构,拥有一个与活动很是类似的生命周期。java

二、为何要使用Fragment?

Activity的使用局限:不能将多个Activity活动界面放在屏幕上一并显示。所以建立了Fragment来弥补Activity的局限。Fragment能够像Activity同样响应Back键等相似Activity的功能。android

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

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

Fragment是直接从Object继承的,而Activity是Context的子类。所以咱们能够得出结论:Fragment不是Activity的扩展。可是与Activity同样,在咱们使用Fragment的时候咱们总会扩展Fragment(或者是她的子类),并能够经过子类更改她的行为。框架

 Fragment能够拥有一个与用户交互的视图层次结构,该视图层次结构和Activity的视图层次结构同样,也能够经过XML布局规范建立(扩充)或者代码建立。(备注:若是视图层次结构须要向用户显示,则必须将Fragment的视图层次结构附加到Activity的试图层次结构中)ide

前面介绍了那么多,只是为了抛砖引玉,接下来进入正题,为何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的当前状态,而不是应该用于初始化她的值)this

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

 在使用Fragment以前,必定要了解Fragment的生命周期。Fragment的生命周期相比Activity的生命周期要更为复杂,理解什么时候处理Fragment相当重要。因为Fragment是依赖于Activity的,接下来看一下二者的生命周期有什么异同,Fragment与Activity生命周期如图1-三、1-4所示:


经过对Fragment和Activity对比,将会发现许多不一样之处,主要缘由是由于Activity和Fragment之间须要交互。 相信你们对Activity的生命周期已经很是熟悉,在此就不作过多介绍,直接切入正题介绍Fragment的生命周期。在Fragment开始阶段,Fragment会以对象的形式存在于内存中。建立Fragment实例有以下两种状况:

  1. 建立Fragment实例;

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

使用代码建立Fragment的实例:

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线程),在实际项目中触发后台线程进行准备很是有用,阻塞调用应该位于后台线程中。)

示例代码:

/**
     * 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 View onCreateView (LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)--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父元素中,该关联会自动完成。若是在此回调中将碎片的视图层次结构附加到父元素,极可能会出现异常。实例代码以下所示:

 /**
     * 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所示:

10.巧妙使用setRetainInstance()

为何会在这儿花必定的篇幅详细说明setRetainInstance()方法呢?由于此方法能够有效地提升系统的运行效率,对流畅性要求较高的应用能够适当采用此方法进行设置。

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

五、怎样管理Fragment? 

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

FragmentManager fragmentManager = getFragmentManager()
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
相关文章
相关标签/搜索