Fragment在一个Activity中表示一个行为或用户界面的一部分,可以在一个Activity中组合多个Fragment形成一个多页的用户界面或者在多个Activity中重复使用一个Fragment,可以认为Fragment是一个拥有独立生命周期,独立接收输入事件,并且可以在Activity运行时添加或删除的模块化部分(有点像”内嵌的Activity“),Fragment不能单独存在:必须依附于Activity并且其生命周期直接受Activity的生命周期影响,通过阅读这一节的内容,将会了解到:
Fragment的使用场景
将一个Activity分成不同的部分,例如分成导航栏和内容块
同一个界面模块可以重复使用时,可以使用Fragment,这样一个Fragment可以在多个Activity中使用,避免在一个Fragment中直接操作另外一个Fragment
Fragment的常用类型
DialogFragment:显示一个浮动的Dialog,这和使用DialogHelper来显示一个Dialog都是可以的,因为你可以把Fragment对话框包含在Activity管理的回退栈中,允许用户返回到关闭的Fragment中(原文,不太理解)
ListFragment:显示一个有Adapter管理的List,和ListActivity相似
- PreferenceFragment:用一个List来显示一组Preference对象,类似PreferenceActivity,在创建一个应用程序的设置界面时可能会用到
创建一个Fragment:(Fragment也有生命周期回调方法,比如onCreate(),onStart(),etc)
一个Fragment至少包含如下三个方法:
onCreate():当Fragment创建时调用,应该初始化一些你想在Fragment暂停或停止时保留的组件
onCreateView():当Fragment第一次绘制UI界面的时候调用,提供一个Layout布局,对布局中的组件的操作就在这里进行(按钮的点击事件等),注意如果Fragment的类型是ListFragment,将会返回一个ListView而不是View
- onPause():当用户离开当前Fragment时调用,不一定会被销毁,但仍有必要在这里做一些保存持久化数据的操作
更多关于Fragment中的回调方法写在下面
将Fragment添加到Activity中的两种方法:
在布局文件中申明Fragment:首先创建一个自定义的Fragment类,这个类中的onCreateView()必须要返回一个View,也就是说必须为它创建一个对应的布局文件,然后在其他布局中将这个Fragment作为一个自定义控件来使用即可,注意,如果有多个Fragment,必须为每一个Fragment提供一个唯一的标识符,如id属性,tag属性
在代码中添加Fragment:在Activity运行的任何时候都可以动态的添加Fragment,只需要指定一个放置Fragment的ViewGroup(例如LinearLayout,FrameLayout等),Activity对Fragment的操作包括添加(add),移除(remove),替换(replace)
使用FragmentTransaction接口实现这些功能。(当然,这里说的Fragment也是指已经创建好的Fragment类对象并在其onCreateView()方法中指定了布局)文档中下一节讲的是没有UI界面的Fragment,我认为暂时不需要用到,所以就不记录,先跳过
管理一些Fragments(通过FragmentManager类):
通过findFragmentById()方法获取一个Activity中的Fragment实例
对Fragment回退栈进行操作,取出或者压入等
- 注册监听事件来监听回退栈的改变,例如addOnBackStackChangedListener()
需要注意一个关于导包的问题:如果导入的是android.app.Fragment,则使用的是:
FragmentManager fm = getFragmentManager();
而如果导入的是android.support.v4.app.Fragment,则应该使用:
FragmentManager fm = getSupportFragmentManager()
操作一些Fragments(通过FragmentTransaction类,主要有add(),remove(),place()等操作):
通过FragmentManager获取FragmentTransaction的实例,接着就能对一些Fragments进行add(),remove(),或place()操作了,最后再执行commit()以提交事务,在执行commit()之前如果执行了addToBackStack()方法,系统将会将执行的这个事务(transaction)存放在回退找中,如果之前有多个操作,比如remove()了多个Fragment,最后再执行commit(),则在返回时(按下返回键)所有这些Fragment都将返回
也就是说,commit()相当于一个节点,你在这个节点之前的操作为一个集合,执行返回的时候这个集合中的Fragment都将返回
比如一个页面有3个Fragment,还有一个按钮,点击事件是将这三个Fragments移除(remove),然后调用addToBackStack(),将它们添加到回退找,然后提交(commit),这样,在点击按钮的时候,这3个fragments都将消失,但是当我点击物理返回键的时候,这3个Fragments都将重新出现在屏幕上,而如果没有执行addToBackStack()方法,则不会返回之前的Fragment。调用commit()方法并不会立即执行事务(transaction),而是会等待UI线程的调度,待UI线程有空闲来执行时才会执行。如果想要让它立即执行,需要在UI线程中使用executePendingTransactions()方法来执行commit提交的事务,但一般没必要这样做,除非这个事务是在其他线程中产生的,需要立即执行,注意:只能在用户离开当前Activity之前使用commit()方法来提交事务,也就是说必须在系统调用Activity的onSavaInstanceState()之前提交事务
Fragment与Activity之前的通信
由于Fragment总是依附于Activity的,所以很多时候需要Fragment和Activity之间进行数据传递等通信,下面是一个简单的例子,用以说明Fragment与Activity之前如何通信:
假设一个Activity中有两个Fragment(Fragment_A和Fragment_B),Fragment_A中是一个列表(list),Fragment_B中是列表单个项目的详情页,也就是说,点击Fragment_A中的某个项目,就会跳转到Fragment_B中显示它的详情,这时就需要通过Activity监听用户对Fragment_A的点击事件,获取到用户点击的项目索引,然后再传递给Fragment_B,这样Fragment_B才知道该显示哪个项目的详情,下面是代码:
- 首先在Fragment_A中定义一个接口 OnItemSelectListener,如下:
public class Fragment_A extends ListFragment {
public interface OnItemSelectListener {
public void onItemSelected(int itemId);
}
//其他方法
}
- 接着在其对应的Activity中实现这个接口并重写其中的回调方法onItemSelected(),在这个回调方法中通知Fragment_B去显示对应项目的详情
public class MainActivity extends AppCompatActivity implements Fragment_A.OnItemSelectListener {
//实现接口中的方法
@Override
public void onItemSelected(int itemId) {
//这里的Fragment_B是已经写好的一个自定义Fragment类
Fragment fmB = new Fragment_B(itemId);
FragmentTransaction ft = getFragmentManager().beginTransaction();
//这里的R.id.fm_b是在主布局中(activity_main.xml)中的一个布局控件的id,用来放置Fragment
ft.replace(R.id.fm_b, fmB);
ft.commit();
}
//其他方法
}
- 然后在Fragment_A的onAttach()方法中(onAttach方法为Fragment生命周期中首先被调用的方法,并且需要当前Activity实例作为参数)实例化OnItemSelectListener接口,获取到对应的Activity(因为这个Activity之前实现了OnItemSelectListener接口)
public class Fragment_A extends ListFragment {
//实例化一个接口对象
public OnItemSelectListener listener;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
listener = (OnItemSelectListener) activity;
}
public interface OnItemSelectListener {
public void onItemSelected(int id);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
//这里的listener指向MainActivity,因此这是调用MainActivity中的onItemSelected()方法
listener.onItemSelected(id);
}
//其他方法
}
通过这样的接口实现,在Fragment_A中获取到Activity的实例,然后Fragment_A就可以在合适的时候调用Activity实例的onItemSelected()方法来达到回调的目的,这样,一个简单的通过回调实现的Fragment与Activity之间的通信就实现了
Fragment中处理菜单点击事件
Fragment也可以像Activity那样使用菜单,只需要在Fragment中实现onCreateOptionMenu()方法即可,这个操作和在Activity中是一样的,注意,为了让Fragment接收到菜单的回调,需要在Fragment的onCreate()方法 中调用setHasOptionMenu(),否则Fragment不能接收到菜单的点击回调,也就是点了菜单没反应,
其实,点击菜单选项这个过程,首先接收到回调的是Activity,如果Activity中没有处理选项点击事件的方法,才会移交给Fragment
处理Fragment的生命周期
就像Activity的生命周期一样,Fragment也存在这三种状态:
Resumed(在运行的Activity中可见状态),
Paused(其他Activity没有完全挡住当前Activity,类似点击分享按钮弹出的一个只遮住一部分屏幕的Activity,后面那个Activity就是处于这种状态),
- Stopped(当前Fragment不可见了,或者所在的Activity不可见了,或者当前Fragment被移除进入回退栈了,但是一个Stopped状态的Fragment依然是活着的,所有的状态都仍然存在,直到其对应的Activity被销毁)
Fragment也具有状态保存的功能,通过在Fragment的onCreate(),onCreateView()或者onActivityCreated()这三个系统回调方法中执行onSaveInstanceState()
Activity与Fragment的生命周期最主要的区别在于他们的回退栈:
Activity的回退栈由系统管理,当用户点击物理返回键时,当前Activity将由系统默认放置到回退栈中
Fragment的回退栈有其所在的Actiity管理,而且只有当明确执行了addToBackStack()方法后,当前Fragment在移除时才会被放入回退栈,否则就销毁。
Fragment的生命周期与Activity是同步的,也就是说,当一个Activity含有一个Fragment的情况下,当用户退出当前应用,系统会先调用Fragment的onPause(),再调用Activity的onPause(),同理,其他几个生命周期回调方法类似,但是因为Fragment的生命周期比Activity多几个独有的方法:
onAttach():当Fragment关联(依附)到一个Activity时调用,也就是在一个Activity中创建了一个Fragment,并调用了commit()之后
onCreateView():当创建Fragment的布局时调用
onActivityCreateed():当对应的Activity中的onCreate()方法返回时(…这里有点搞不明白了,Activity中的onCreate()怎么返回?)
onDestroyView():当Fragment的布局被移除时调用
- onDetach():当Fragment与Activity断开联系时调用
下面两张图来自Android官方文档,左图为Fragment的生命周期图,右图为Activity的状态与Fragment的生命周期对比图
————————