一、Fragment的概念和用法:
(一)、概念:
Fragment是在Android 3.0 (API level 11)开始引入新的API技术。
为了提高代码重用性和改善用户体验,我们将Activity中的UI组件进行分组和模块化管理。这些分组后的UI组件就是Fragment。
一个Activity页面中可以包含多个Fragment模块,而同一个Fragment模块也可以被多个Activity使用。每个Fragment有自己的布局,有自己的生命周期。虽然Fragment拥有自己的生命周期,但因为Fragment必须被嵌入到Activity中使用,因此Fragment的生命周期是受其Activity宿主的生命周期所控制的。当Activity暂停时,该Activtiy内的所有Fragment都会暂停;当Activity被销毁时,该Activity内的所有Fragment都会被销毁。
(二)、Fragment为什么能改善用户体验,另外Fragment能用于平板电脑,那么普通的手机屏幕适用吗?
下图是一个“AndroidManual学习手册浏览界面”的效果图:
如上图所示,“平板电脑”中,在一个Activity布局中放了两个Fragment:左侧是文章标题列表Fragment,右侧是文章内容展示的Fragment。左侧的标题列表不变,点击每条列表项,右侧的文章内容发生变化。点击和浏览内容都在同一个页面中发生,避免了页面切换和点击“返回键”返回的操作。用户感觉自然方便和快捷。
那么Fragment组件适用于普通屏幕的手机吗?答案是肯定的。Fragment本身是可复用的组件。是否在一个Activity页面中放置多个Fragment取决了屏幕的大小,如果屏幕大小不够,那么就可以在Activity A中只包含Fragment A,在Activity B中只包含Fragment B,点击A中的item跳转到B就可以。
(三)、Fragment要点:【
重点】
1、Fragment作为Activity界面的一部分组成出现;
2、可以在一个Activity中同时出现多个Fragment,并且,一个Fragment亦可在多个Activity中使用;
3、在Activity运行过程中,可以
添加、
移除或者
替换Fragment(
add()、
remove()、
replace());
4、Fragment可以响应自己的输入事件,并且有自己的生命周期,当然,它们的生命周期直接受其所属的宿主Activity的生命周期控制。
二、创建Fragment:
(一)、创建Fragment的步骤:【只需要两步】
1、创建一个Fragment,必须继承Fragment 这个基类或其子类;
【备注:】除了继承基类 Fragment , 还有一些子类你可能会继承,是哪些子类呢?
- DialogFragment
-
- 显示一个浮动的对话框. 用这个类来创建一个对话框,是使用在Activity类的对话框工具方法之外的一个好的选择,因为你可以将一个fragment对话框合并到activity管理的fragment back stack中,允许用户返回到一个之前曾被摒弃的fragment.
- ListFragment
-
- 显示一个由一个adapter(例如 SimpleCursorAdapter)管理的项目的列表, 类似于ListActivity。它提供一些方法来管理一个list view, 例如 onListItemClick()回调来处理点击事件.
- PreferenceFragment
-
- 显示一个 Preference对象的层次结构的列表, 类似于PreferenceActivity。这在为你的应用创建一个"设置"activity时有用处.
2、实现回调方法:
Fragment类的代码看起来很像 Activity 。它包含了和activity类似的回调方法, 例如onCreate()、 onStart()、onPause()以及 onStop()。事实上,,如果你准备将一个现成的Android应用转换到使用Fragment,可能只需要将代码从你的Activity的回调方法中分别移动到你的Fragment的回调方法即可。
通常,应当至少实现如下的生命周期方法:
- onCreate()
当创建fragment时, 系统调用该方法.
在实现代码中,应当初始化想要在fragment中保持的必要组件, 当fragment被暂停或者停止后可以恢复. - onCreateView()
fragment第一次绘制它的用户界面的时候, 系统会调用此方法. 为了绘制fragment的UI,此方法必须返回一个View, 这个view是你的fragment布局的根view. 如果fragment不提供UI, 可以返回null. - onPause()
用户将要离开fragment时,系统调用这个方法作为第一个指示(然而它不总是意味着fragment将被销毁.) 。在当前用户会话结束之前,通常应当在这里提交任何应该持久化的变化(因为用户有可能不会返回).
【备注:】对于大部分Fragment而言,通常实现三个回调方法即可。但是实际开发中可以根据需要重写Fragment生命周期中的任意回调方法。
(二)、Fragment实例:
1、创建fragment步骤:
2、主页面布局及Fragment布局文件代码:
Fragment管关键代码如下:
Activity布局界面关键代码如下:
【备注:】
- <fragment>标签的android:id 和 android:tag属性必须至少有一个,同一个xml文件中id或tag必须唯一;
- <fragment>标签的class 和 android:name属性功能完全相同,使用其中任何一个即可。
三、动态创建Fragment:
(一)、概念:
如果将Fragment写在布局文件中,那么就是静态创建fragment;如果没有在布局文件中写<fragment>标签,而是在java文件中通过实例化Fragment创建Fragment的方式就是动态创建Fragment。
(二)、动态创建Fragment的步骤:
1、fragment文件代码:
2、MainActivity文件代码:
三、Fragment生命周期:
(一)、Fragment基本状态:
1、活动状态:Resumed 当前Fragment位于前台,用户可见,可以获得焦点;
2、暂停状态: Paused 另一个Activity处于前台并拥有焦点, 但是该Fragment所在的Activity仍然可见(前台Activity局部透明或者没有覆盖整个屏幕),不过不能获得焦点;
3、停止状态:Stopped
- 要么是宿主Activity已经被停止, 要么是Fragment从Activity被移除但被添加到回退栈中;
- 停止状态的Fragment仍然活着(所有状态和成员信息被系统保持着)。 然而, 它对用户不再可见, 并且如果Activity被销毁,它也会被销毁;
4、销毁状态:Destroyed 只能等待被回收。
(二)、Fragment生命周期:【重点】
1、onAttach(): 当该Fragment被添加到Activity时被回调。该方法只会被调用一次;
2、onCreate(): 当创建Fragment时被回调。该方法只会被调用一次;
3、onCreateView():每次创建、绘制该Fragment的View组件时回调该方法,Fragment将会显示该方法返回的View 组件;
4、onActivityCreated(): 当Fragment的宿主Activity被启动完成后回调该方法;
5、onStart(): 启动Fragment时被回调;
6、onResume(): onStart()方法后一定会回调onResume()方法;
7、onPause(): 暂停Fragment时被回调;
8、onStop(): 停止Fragment时被回调;
9、onDestroyView(): 销毁该Fragment所包含的View组件时调用;
10、onDestroy(): 销毁Fragment时被回调。该方法只会被调用一次;
11、onDetach(): 将Fragment从Activity中删除、替换完成时调用该方法。onDestroy()方法后一定会回调onDetach()方法。
该方法只会被调用一次。
12、onInflate():
13、onViewCreated():
(三)、运行结果观察:
1、第一次加载Fragment:
2、在MainActivity中,点击“返回键”退出程序:
3、在MainActivity中,点击“HOME键”退出程序:
【备注:】请注意第二种和第三种情况的区别。如果点HOME键退出,则少调用了onDestroyView()、onDestroy()、onDetach()三个回调方法。
4、点击“下一页”,进入NextActivity页面:
5、在NextActivity页面中,点击“HOME键”退出程序:
6、上次HOME键退出后,重新进入程序:【因为上次是从NextActivity页面按HOME退出,所以此时直接进入 NextActivity页面 】
(四)、跟Fragment生命周期相关的其他方法:
1、onInflate():在onCreate()方法之前调用,这时窗体上的控件都没有被创建,所以不能通过getActivity().findViewById(),因为此时getActivity()返回null。
2、onViewCreated():在onCreateView()方法后会立刻调用onViewCreated()方法。通常在该方法中完成创建Fragment的最后工作,然后系统就开始调用onCreate()方法对窗体初始化。
(五)、如果Activity生命周期和Fragment生命周期的Log都开启,那么依次会如何输出信息呢?
1、第一次加载MainActivity页面:
2、在MainActivity页面中,点击“返回键”退出程序:
3、在MainActivity中,点击“HOME键”退出程序:
【备注:】请同学们注意观察Activity回调方法和Fragment回调方法执行的顺序。请设想各种操作步骤,写出生命周期回调方法执行的顺序。
【特别强调:】
当Activity的onCreate()方法中,如果将Log的位置放在setContentView(R.layout.activity_main)之前,那么Activity的onCreate()方法就会在最先执行,如果将日志写在setContentView(R.layout.activity_main)之后,那么Activity的onCreate()方法就会在Fragment的onViewCreated()之后执行。
4、分析生命周期执行过程的案例:
开启一个包含有Fragment的Activity页面,然后点击“返回键”退出,然后再次启动该Activity页面,请描述完整的生命周期回调方法。
参考答案:
04:36:31.282: MainActivity: ==onCreate() 04:36:31.502: LeftFragment: ==onInflate()执行了 04:36:31.502: LeftFragment: ==onAttach()执行了 04:36:31.513: LeftFragment: ==onCreate()执行了 04:36:31.513: LeftFragment: ==onCreateView()执行了 04:36:31.533: LeftFragment: ==onViewCreated()执行了 04:36:31.542: LeftFragment: ==onActivityCreated()执行了 04:36:31.542: MainActivity: ==onStart() 04:36:31.552: LeftFragment: ==onStart()执行了 04:36:31.563: MainActivity: ==onResume() 04:36:31.563: LeftFragment: ==onResume()执行了 04:37:20.133: LeftFragment: ==onPause()执行了 04:37:20.142: MainActivity: ==onPause() 04:37:21.932: LeftFragment: ==onStop()执行了 04:37:21.932: MainActivity: ==onStop() 04:37:21.942: LeftFragment: ==onDestroyView()执行了 04:37:21.952: LeftFragment: ==onDestroy()执行了 04:37:21.952: LeftFragment: ==onDetach()执行了 04:37:21.952: MainActivity: ==onDestroy() 04:39:20.763: MainActivity: ==onCreate() 04:39:21.552: LeftFragment: ==onInflate()执行了 04:39:21.635: LeftFragment: ==onAttach()执行了 04:39:21.635: LeftFragment: ==onCreate()执行了 04:39:21.652: LeftFragment: ==onCreateView()执行了 04:39:21.652: LeftFragment: ==onViewCreated()执行了 04:39:21.842: LeftFragment: ==onActivityCreated()执行了 04:39:21.842: MainActivity: ==onStart() 04:39:21.908: LeftFragment: ==onStart()执行了 04:39:21.912: MainActivity: ==onResume() 04:39:21.932: LeftFragment: ==onResume()执行了
四、android-support-v4.jar下的fragment的使用
activity中的关键代码:
五、Activity与Fragment之间的数据交互:
(一)、Activity向Fragment传递数据:Arguments
1、做法:
可以通过Fragment.setArguments()方法向Fragment传递数据,并且通过getArguments()方法获取传递的数据。
2、
MainActivity文件代码:
3、
Fragment文件代码:
4、注意事项:
- Supply the construction arguments for this fragment. This can only be called before the fragment has been attached to its activity; that is, you should call it immediately after constructing the fragment. The arguments supplied here will be retained across fragment destroy and creation.
- 翻译:setArguments(bundle)为fragment提供构造的参数。它只能在fragment回调onAttach()方法之前调用。就是说,你应该在构造完fragment之后立刻调用它。提供的这些参数将在Fragment被销毁前一直保留。
- 在布局文件中使用<fragment>标签声明的Fragment(也就是静态生成的Fragment),都不能使用setArguments方法设置Bundle对象。
(二)、Fragment向宿主Activity回传信息:(Fragment
回调机制)
1、原则:
Fragment类要尽量保证其独立性,Fragment类中不应该有访问其他Fragment和Activity中资源的代码,否则这个Fragment就不能在不改动代码的情况下用在其他地方。
如何让多个Fragment之间可以独立多次使用,而不是紧密地绑定到一起?通常的做法就是在Fragment类中编写一个接口,然后在该Fragment的宿主窗口类中实现该接口。这样Fragment与其宿主就实现了信息交互。
2、Fragment接口回调的步骤:(五步曲)
- 在Fragment文件中定义接口:OnItemClickedListener,定义抽象方法onClick(String info);
- 在Fragment文件中定义属性:private OnItemClickedListener mylistener;
- 在Fragment文件中的onAttach()方法中执行:mylistener = (OnItemClickedListener) getActivity();
- 在Fragment文件中,给某个控件增加监听器,在监听器中执行:mylistener.onClick(“需要传递的数据”);
- 将MainActivity实现OnItemClickedListener,重写onClick(String info)方法。参数就是Fragment传递的数据信息,在该方法中执行希望的逻辑操作。
3、Fragment的核心代码:
4、MainActivity的核心代码:
五、FragmentManager与Fragment事务: 【重要】
(一)、概念:
Activity管理Fragment主要依靠FragmentManager。FragmentManager可以完成以下几方面的功能:
- 使用findFragmentById()或findFragmentByTag()方法来获取指定Fragment;
- 调用popBackStack()方法将Fragment从回退栈中弹出(如同用户按下“返回键”的效果);
- 调用addOnBackStackChangeListener()注册一个监听器,用于监听回退栈的变化情况。
在Activity中使用Fragment,一个很明显的特性是:根据用户的交互情况,可以对Fragment进行添加、移除、替换,以及执行其他动作,提交给Activity的每一套变化被称为一个事务。Fragment事务代表了Activity对Fragment执行的多个改变操作。实现事务借助于FragmentTransaction对象。我们可以保存每一个事务到一个Activity管理的backstack,这样用户就能由Fragment的变化往回导航。
【思考:】
不加事务会怎么样呢?
如果不加事务,那么当点击“返回键”,页面会返回上一个页面。对于本例来说,只有一个MainActivity页面,所以一旦点击“返回键”则立刻退出程序,而用户实际上希望返回上次查看的文章。如果加上事务,则用户可以返回到每次查看过的文章。
(二)、Fragment与回退栈(导航):
1、回退栈的概念:BackStack
在Activity中已经探讨过回退栈,这种数据结构用来存放创建的窗口对象,并根据一定的规则决定哪些窗口对象应该出栈,凡是出栈的窗口对象将被销毁,也就是说窗口对象从入栈到出栈完成了窗口的整个生命周期。回退栈不仅能存储窗口对象,还可以存储Fragment对象。
(三)、FragmentManager与FragmentTransaction示例代码:
Fragment的布局文件:
// 构建Bundle对象,将文章id放在其中。 Bundle bundle = new Bundle(); bundle.putString("titleId", id); // 构建文章内容的Fragment RightFragment fragment = new RightFragment(); // 通过fragment对象的setArguments(bundle)方法将数据传输给其他Fragment。 fragment.setArguments(bundle); // 通过getFragmentManager()方法构建FragmentManager对象 FragmentManager fragManager = getFragmentManager(); // 通过FragmentManager对象的beginTransaction()方法构建FragmentTransaction对象 FragmentTransaction trans = fragManager.beginTransaction(); // 将主页面布局文件中的ViewGroup容器替换成文章内容Fragment trans.replace(R.id.fragment_content, fragment); trans.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE); // 将事务添加到回退栈,这样用户就可以通过“返回键”回到替换Fragment的上一个状态 trans.addToBackStack(null); // 提交事务 trans.commit();