- 博客已经迁移到 HankinsPan的个人博客
皇帝:“你们丐帮几千万人,一天不解散,我怎么心安?”
苏乞儿:“丐帮有多少人不是我决定的,是你决定的。如果你真的英明神武,让百姓安居乐业,鬼才愿意当乞丐呢!”
——《武状元苏乞儿》
引
正如上面引用的这一句,我喜欢把Android的Fragment比作是武林中的一个帮派——丐帮。之所以这样来分配,因为我认为Fragment是出自Android四大天王之首的Activity,而Activity就好是天下的武宗——少林。正所谓:少林,丐帮本一家。少林有达摩这样的绝世高手,72绝技,大小无相功,龙抓手这样的绝招让武林中人望其项背。而丐帮也有让天下人闻而胆寒的降龙十八掌,打狗棍法。
把Fragment比作丐帮的另一个原因是因为Fragment的生命周期众多,和丐帮的人数众多很像。所以,不妨就探探这天下第一帮的前世今生吧。
前世
Fragment的设计理念
Android在3.0中引入了fragments的概念,主要目的是用在大屏幕设备上–例如平板电脑上,支持更加动态和灵活的UI设计。平板电脑的屏幕要比手机的大得多,有更多的空间来放更多的UI组件,并且这些组件之间会产生更多的交互。Fragment允许这样的一种设计,而不需要你亲自来管理 viewhierarchy的复杂变化。 通过将activity的布局分散到fragment中, 你可以在运行时修改activity的外观,并在由activity管理的back stack中保存那些变化.
fragment在你的应用中应当是一个模块化和可重用的组件.即,因为fragment定义了它自己的布局, 以及通过使用它自己的生命周期回调方法定义了它自己的行为,你可以将fragment包含到多个activity中. 这点特别重要, 因为这允许你将你的用户体验适配到不同的屏幕尺寸.举个例子,你可能会仅当在屏幕尺寸足够大时,在一个activity中包含多个fragment,并且,当不属于这种情况时,会启动另一个单独的,使用不同fragment的activity.
Fragment的生命周期
我们下面以Fragment的生命流程为主,伴着Activity的生命周期来讲;
onAttach:onAttach()在fragment与Activity关联之后调调查用。需要注意的是,初始化fragment参数可以从getArguments()获得,但是,当Fragment附加到Activity之后,就无法再调用setArguments()。所以除了在最开始时,其它时间都无法向初始化参数添加内容。有关Fragment参数初始化及传递的问题,我们会在后面的篇章中细讲。
onCreate:fragment初次创建时调用。尽管它看起来像是Activity的OnCreate()函数,但这个只是用来创建Fragment的。此时的Activity还没有创建完成,因为我们的Fragment也是Activity创建的一部分。所以如果你想在这里使用Activity中的一些资源,将会获取不到。比如:获取同一个Activity中其它Frament的控件实例。如果想要获得Activity相关联的资源,必须在onActivityCreated中获取。
具体原因参见onActivityCreated;
/**
* 如果把这段代码放到Fragment的onCreate()中,那么btnTry的值将会是NULL。
* 注意:getActivity()是有值的,因为一旦Activity与Fragment Attached就可以通过getActivity()来获取相关联* 的Activity的实例。
*/
Button btnTry = getActivity().findViewById(R.id.btn_try);
- onCreateView:在这个fragment构造它的用户接口视图(即布局)时调用。在这里期望返回此Fragment的一个视图层次结构。使用LayoutInflater的inflater()方法来构造实图。代码如下:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment3, container, false);
}
其中LayoutInflater::inflate()的函数声明如下:
public View inflate(int resource, ViewGroup root, boolean attachToRoot)
其中第二个参数: Container,可以为NULL,如果父窗口值为NULL,这意味着该Fragment不可见,因为它没有附加到任何的视图层次中。
最后一个参数:boolean attachToRoot:一般为FALSE,它的意义为,是否当前XML的根结点作为整个APP视图的根结点,如果设为TRUE,那返回的VIEW将是整个APP视图架构的根结点,我们一般不需要这么做。因为我们的Fragment是依附于Activity的,Activity上可能有其它控件,我们的Fragment只是其中的一个小分支,如果这里设为TRUE,那么Fragment以外的分支将被全部删除,整个APP视图将会只有Fragment;
- onActivityCreated:在Activity的OnCreate()结束后,会调用此方法。所以到这里的时候,Activity已经创建完成!在这个函数中才可以使用Activity的所有资源。如果把下面的代码放在这里,获取到的btn_Try的值将不会再是空的!
Button btnTry = getActivity().findViewById(R.id.btn_try);
onStart:当到OnStart()时,Fragment对用户就是可见的了。但用户还未开始与Fragment交互。在生命周期中也可以看到Fragment的OnStart()过程与Activity的OnStart()过程是绑定的。意义即是一样的。以前你写在Activity的OnStart()中来处理的代码,用Fragment来实现时,依然可以放在OnStart()中来处理。
onResume:当这个fragment对用户可见并且正在运行时调用。这是Fragment与用户交互之前的最后一个回调。从生命周期对比中,可以看到,Fragment的OnResume与Activity的OnResume是相互绑定的,意义是一样的。它依赖于包含它的activity的Activity.onResume。当OnResume()结束后,就可以正式与用户交互了。
onPause:此回调与Activity的OnPause()相绑定,与Activity的OnPause()意义一样。
onStop:这个回调与Activity的OnStop()相绑定,意义一样。已停止的Fragment可以直接返回到OnStart()回调,然后调用OnResume()。
onDestroyView:如果Fragment即将被结束或保存,那么撤销方向上的下一个回调将是onDestoryView()。会将在onCreateView创建的视图与这个fragment分离。下次这个fragment若要显示,那么将会创建新视图。这会在onStop之后和onDestroy之前调用。这个方法的调用同onCreateView是否返回非null视图无关。它会潜在的在这个视图状态被保存之后以及它被它的父视图回收之前调用。
onDestroy:当这个fragment不再使用时调用。需要注意的是,它即使经过了onDestroy()阶段,但仍然能从Activity中找到,因为它还没有Detach。
onDetach:Fragment生命周期中最后一个回调是onDetach()。调用它以后,Fragment就不再与Activity相绑定,它也不再拥有视图层次结构,它的所有资源都将被释放。
Fragment 发家(基本使用方法)
静态添加
Fragment的静态添加比较简单,可以直接把Fragment当成普通的控件在Activity的布局文件中加入,以不同的Fragment名字来链接就可以。- 继承Fragment 重写onCreateView 来决定Fragment的布局。
- 在Activity中声明此Fragment,和View 使用方式相同。
动态添加
动态添加Fragment是Fragment的多数使用方式。可以动态的添加、删除、修改Fragment。这样的话,程序的界面就会变得更加的多样化。
动态的添加Fragment主要分为4步:- 获取到FragmentManager,在Activity中可以直接通过getFragmentManager()得到。
- 开启一个事务,通过调用beginTransaction方法开启。
- 向容器内加入Fragment,一般使用replace()方法实现。一般传入容器id,fragment实例。
- 提交事务,调用commit()方法提交。
blankFragment = new BlankFragment();
fm = getFragmentManager();
FragmentTranscation transcation = fm.benginTranscation();
transcation.repleac(R.id.id_fragment,blankFragment);
transcation.commit();
=>
getFragmentManager().beginTranscation().replace(id,fragment).commit();
动态添加的解释
FragmentManager
要管理activity中的fragments,你就需要使用FragmentManager。通过getFragmentManager()或getSupportFragmentManager()获得
常用的方法有:
manager.findFragmentById(); //根据ID来找到对应的Fragment实例,主要用在静态添加fragment的布局中,因为静态添加的fragment才会有ID
manager.findFragmentByTag();//根据TAG找到对应的Fragment实例,主要用于在动态添加的fragment中,根据TAG来找到fragment实例
manager.getFragments();//获取所有被ADD进Activity中的FragmentFragmentTransaction
一般用来对当前的Fragment进行管理,包括add,replace,remove;
常用的针对Fragment的方法有:
//将一个fragment实例添加到Activity的最上层
add(int containerViewId, Fragment fragment, String tag);
//将一个fragment实例从Activity的fragment队列中删除
remove(Fragment fragment);
//替换containerViewId中的fragment实例,注意,它首先把containerViewId中所有fragment删除,然后再add进去当前的fragment
replace(int containerViewId, Fragment fragment);Transaction事务回退的原则
这里我们着重讲一下,回退是以commit()提交的一次事务为单位的,而不是以其中的add,replace等等操作为单位回退的,即,如果我们在一次提交是添加了fragment2,fragment3,fragment4,那么回退时,会依据添加时的顺序,将它们一个个删除,返回到没有添加fragment4,fragment3,fragment2的状态。
Fragment 的常用招数(API)
transaction.add() 往Activity中添加一个Fragment
transaction.remove() 从Activity中移除一个Fragment,如果被移除的Fragment没有添加到回退栈,这个Fragment实例将会被销毁。
transaction.replace() 使用另一个Fragment替换当前的,实际上就是remove()然后add()的合体~
transaction.hide() 隐藏当前的Fragment,仅仅是设为不可见,并不会销毁
transaction.show() 显示之前隐藏的Fragment
detach() 会将view从UI中移除,和remove()不同,此时fragment的状态依然由FragmentManager维护。
attach() 重建view视图,附加到UI上并显示。
transatcion.commit() 提交一个事务
Fragment 回退栈
在transaction.commit()之前,使用addToBackStack()将其添加到回退栈中。
transaction.addToBackStack(String tag);
在需要回退时,使用popBackStack()将最上层的操作弹出回退栈。
manager.popBackStack();
这里的popBackStack()是弹出默认的最上层的栈顶内容。
当栈中有多层时,我们可以根据id或TAG标识来指定弹出到的操作所在层。函数如下:
void popBackStack(int id, int flags);
void popBackStack(String name, int flags);
其中
- 参数int id是当提交变更时transaction.commit()的返回值。
- 参数string name是transaction.addToBackStack(String tag)中的tag值;
- 至于int flags有两个取值:0或FragmentManager.POP_BACK_STACK_INCLUSIVE;
- 当取值0时,表示除了参数一指定这一层之上的所有层都退出栈,指定的这一层为栈顶层;
- 当取值POP_BACK_STACK_INCLUSIVE时,表示连着参数一指定的这一层一起退出栈;
popBackStackImmediate()
popBackStackImmediate(String tag)
popBackStackImmediate(String tag, int flag)
popBackStackImmediate(int id, int flag)
- Fragment回退栈监听
FragmentManager还为我们提供了监控回退栈状态改变的方法:
FragmentManager::addOnBackStackChangedListener(listener);//添加监听器
FragmentManager::removeOnBackStackChangedListener(listener);//移除监听器
通过添加监听器,就可以在回退栈内容改变时,及时收到通知;