一. Fragment简介
我们都知道,Android上的界面展示都是通过Activity实现的,Activity实在是太常用了,我相信大家都已经非常熟悉了,这里就不再赘述。但是Activity也有它的局限性,同样的界面在手机上显示可能很好看,在平板上就未必了,因为平板的屏幕非常大,手机的界面放在平板上可能会有过分被拉长、控件间距过大等情况。这个时候更好的体验效果是在Activity中嵌入"小Activity",然后每个"小Activity"又可以拥有自己的布局。因此,我们今天的主角Fragment登场了。
Android在3.0版本引入了Fragment功能, Fragment根据词海的翻译可以译为:碎片、片段。Fragment不可以单独存在,是一种可以嵌入在活动当中的UI片段,它能让程序更加合理和充分地利用大屏幕,相比Activity更轻量级、更灵活。它非常类似于Activity,可以像Activity一样包含布局。Fragment通常是嵌套在Activity中使用的。
Fragment是在3.0版本引入的,如果你使用的是3.0之前的系统,需要先导入android-support-v4的jar包才能使用Fragment功能。
二. Fragment的生命周期
- 每一个fragments 都有自己的一套生命周期回调方法和处理自己的用户输入事件。 对应生命周期可参考下图:
因为Fragment必须嵌入在Acitivity中使用,所以Fragment的生命周期和它所在的Activity是密切相关的。如果Activity是暂停状态,其中所有的Fragment都是暂停状态;如果Activity是stopped状态,这个Activity中所有的Fragment都不能被启动;如果Activity被销毁,那么它其中的所有Fragment都会被销毁。但是,当Activity在活动状态,可以独立控制Fragment的状态,比如加上或者移除Fragment。
1. Fragment的几种状态
Fragment和activity一样,也是有四种状态
- 活动状态:Resumed
当前Fragment位于前台,用户可见,可以获得焦点; - 暂停状态: Paused
另一个Activity处于前台并拥有焦点, 但是该Fragment所在的Activity仍然可见(前台Activity局部透明或者没有覆盖整个屏幕),不过不能获得焦点; - 停止状态:Stopped
要么是宿主Activity已经被停止, 要么是Fragment从Activity被移除但被添加到回退栈中;停止状态的Fragment仍然活着(所有状态和成员信息被系统保持着)。 然而, 它对用户不再可见, 并且如果Activity被销毁,它也会被销毁; - 销毁状态:Destroyed 只能等待被回收。
2.Fragment和Activity的对比
(1)活动和碎片之间的对比图
(2)由上图可以看出,Fragment比Activity多了几个额外的生命周期回调方法:
- onAttach() 当碎片和活动建立关联时调用。(获得activity的传递的值)
- onCreateView() 为碎片创建视图调用就是加载布局时。
- onActivityCreated() 确保与碎片相关联的活动一定已经创建完毕的时候调用。
- onDestroyView() 当与碎片的视图被移除的时候调用。
- onDetach()当碎片与活动解除关联的时候调用。
三.如何使用Fragment
-
静态使用
- 继承Fragment,重写onCreateView决定Fragemnt的布局
- 在Activity中声明此Fragment,就当和普通的View一样
关键代码:
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(layout, container, false); }
-
动态添加
1.创建待添加的碎片实例
2.获取到FragmentManager,在活动中可以直接调用getFragmentManager()方法得到。
3.开启一个事务,通过调用beginTransaction()方法开启
4.向容器内添加碎片,一般使用replace()方法,需要传入容器的id和待添加的碎片实例。
5.提交事务,调用commit()(方法来完成。
关键代码:
FragmentManager fm = getFragmentManager();
// 开启Fragment事务
FragmentTransaction transaction = fm.beginTransaction();
//Fragment的布局替代控件
transaction.replace(控件的id, 碎片实例);
// 事务提交
transaction.commit();
-
在碎片中模拟返回栈
通过点击按钮添加了一个碎片之后,这时按下back键程序就会直接退出。如果这里我们想模仿类似返回栈的效果,按下back键可以返回到上一个碎片,在FragmentTransaction中提供了一个addToBackStack()方法,可以用于将一个事务添加到返回栈中
关键代码:
transaction.addToBackStack(null);
注意: 在事务没有提交之前添加代码
四.Fragment的通信
Fragment和Activity之间的调用
-
活动中调用碎片的方法:
为了方便碎片与活动之间进行通信,FragmentManager提供了一个类似于findViewById()的方法,专门从布局文件中获取碎片的实例,代码如下:
Fragment fragment=(Fragment)getFragmentManager()
.findFragmentById(R.id.fragment);
调用 FragmentManager 的 findFragmentById()方法,可以在活动中得到相应碎片的实例,这样就可以调用碎片的方法了。
-
碎片中调用活动的方法:
在每个碎片中都可以通过调用 getActivity()方法来得到和当前碎片相关联 的活动实例,代码如下:
MainActivity activity=(MainActivity)getActivity();
获得活动实例后,就可以在碎片中调用活动的方法。另外当碎片中需要 使用 Context 对象时,也可以使用 getActivity()方法,因为获取到的活动本身就是一个Context对象了。
Fragment和Activity之间的通信
- ** Handler方案**
public class MainActivity extends FragmentActivity{ //声明一个Handler public Handler mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
...相应的处理代码
}
}
...相应的处理代码
}
public class MainFragment extends Fragment{
//保存Activity传递的handler
private Handler mHandler;
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//这个地方已经产生了耦合,若还有其他的activity,这个地方就得修改
if(activity instance MainActivity){
mHandler = ((MainActivity)activity).mHandler;
}
}
...相应的处理代码
}
该方案存在的缺点:
Fragment对具体的Activity存在耦合,不利于Fragment复用
不利于维护,若想删除相应的Activity,Fragment也得改动
没法获取Activity的返回数据
-
EventBus方案
主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,线程之间传递消息.优点是开销小,代码更优雅。以及将发送者和接收者解耦。
1、下载EventBus的类库源码:https://github.com/greenrobot/EventBus
2、基本使用
(1)自定义一个类,可以是空类,比如:
public class AnyEventType {
public AnyEventType(){}
}
(2)在要接收消息的页面注册:
eventBus.register(this);
(3)发送消息
eventBus.post(new AnyEventType event);
(4)接受消息的页面实现(共有四个函数,各功能不同,这是其中之一,可以选择性的实现,这里先实现一个):
public void onEvent(AnyEventType event) {}
(5)解除注册
eventBus.unregister(this);
注意:在EventBus中,获取实例的方法一般是采用EventBus.getInstance()来获取默认的EventBus实例,当然你也可以new一个实例。
- setArguments(Bundle bundle)方案:
public class ExampleFragment extends Fragment{
private String type;
public static ExampleFragment newInstance(String type) {
ExampleFragment myFragment = new ExampleFragment();
Bundle args = new Bundle();
args.putString("type", type);
//发送要传递的值
myFragment.setArguments(args);
return myFragment;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//接收数据
type=getArguments().getString("type");
...相应的处理代码
}
}
这种activity向fragment传值方法是最简单有效的,而且在fragment异常重建的时候bundle将参数保存了下来
- ** 使用接口回调的方法通信(推荐)**
//MainActivity 实现MainFragment的接口
public class MainActivity extends FragmentActivity implements FragmentListener{
@override
public void toInterface (){ } ...其他处理代码省略 }
public class MainFragment extends Fragment{
public FragmentListener mListener;
//MainFragment开放的接口
public static interface FragmentListener{
void toInterface (); }
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
//对传递进来的Activity进行接口转换
if(activity instance FragmentListener){
mListener = ((FragmentListener)activity);
} } ...其他处理代码省略 }
这种方案应该是既能达到复用,又能达到很好的可维护性,并且性能很好(推荐使用)