经过反复的学习对比,个人觉得带着问题学习新知是最有效的学习方式,因此文本就以提问的方式来讲述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-3 Activity运行时Fragment生命周期 图1-4 Activity生命周期
通过对Fragment和Activity对比,将会发现许多不同之处,主要原因是因为Activity和Fragment之间需要交互。 相信大家对Activity的生命周期已经非常熟悉,在此就不做过多介绍,直接切入正题介绍Fragment的生命周期。在Fragment开始阶段,Fragment会以对象的形式存在于内存中。创建Fragment实例有如下两种情况:
- 创建Fragment实例;
- 系统从保存状态重新创建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的一个视图层次结构。
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()回调
9.onDetach()回调
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()方法来获取碎片管理器。代码如下所示:
- FragmentManager fragmentManager = getFragmentManager()
- FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();