主要内容:
- 1、Fragment 的生命周期
- 2、Fragment 的管理
- 3、Fragment 的空参构造方法
- 4、Fragment PagerAdapter 与 FragmentStatePagerAdapter
- 5、Fragment 间的数据传递
- 6、Fragment 与 Activity 通信
- 7、Fragment 与屏幕旋转
- 8、Fragment 与菜单栏
- 9、Fragment 与对话框
一、Fragment 的生命周期
Fragment 的生命周期主要有下面几种:
- onAttach - onCreate - onCreateView - onActivityCreate - onStart - onResume
- onPause - onStop -( onSaveInstanceState )- onDestroyView - onDestroy - onDetach
和 Activity 相比,Fragment 多了许多可在流程中操作的接口,让我们对于用户界面的控制更加灵活。在安卓开发中,有许多要用到关乎 Activity 生命周期的操作,由于 Fragment 是依赖于 Activity 存在的,因此它的生命周期与 Activtiy 的表现一致。下面是常见的用户操作:
//按下 home 键后返回界面
- onPause - onStop
- onStart - onResume
//按下返回键
- onPause - onStop -( onSaveInstanceState )- onDestroyView - onDestroy - onDetach
//屏幕旋转
- onPause - onStop -( onSaveInstanceState )- onDestroyView - onDestroy - onDetach
- onAttach - onCreate - onCreateView - onActivityCreate - onStart - onResume
如果 Fragment 被加入回退栈,那么上面的按下返回键时,只执行到 onDestroyView。
二、Fragment 的管理
在开发中,我们通常使用 FragmentManager 和 FragmentTransaction 来管理界面中的 Fragment。通常,我们用下面的代码操作 Fragment。
FragmentTest fragmentTest = FragmentTest.getInstance();
FragmentManager fm = getSupportFragmentManager();
// 获得事件
FragmentTransaction ft = fm.beginTransaction();
// 添加
ft.add(R.id.activity_containerId, fragmentTest ,"TEST");
// 删除
ft.remove(fragmentTest);
// 替换 功能相当于 添加+删除
ft.replace(R.id.activity_containerId, fragmentTest ,"TEST");
// 显示
ft.show(fragmentTest);
// 隐藏
ft.hide(fragmentTest)
// 提交
ft.commit();
其中,添加和替换操作都需要重新创建这个 Fragment 对象,而显示和隐藏操作不需要,因此,在碎片不多的情况下,我们为了保存碎片中的数据,很多时候会用这种方法对 Fragment 进行控制。Fragment 是个碎片,但是在很多情况下,它几乎充满了整个 Activity,我们常常会有这样的需求,在按下回退键的时候,可以回到上一个 Fragment,这时我们需要一个类似 Activity 的栈存储系统,在 Fragment 中的处理方式是在 FragmentTransaction 提交之前,调用 ft.addToBackStack()。
三、Fragment 的空参构造方法
谷歌规定 Fragment 必须要有一个空参公共的构造器。然而,我们通常不用这个构造器获得实例。这是为什么呢,官网中对 Fragment 的说明给了我们答案:官网文档链接
原文翻译:
每一个 Fragment 必须有一个空的构造方法,这样当 Activity 恢复状态时
Fragment 能够被实例化。强烈建议当我们继承 Fragment 类时,不要添加带有参数的构造方法,因为当Fragment被重新实例化时,这些构造方法不会被调用。如果需要给 Fragment 传递参数,可以调用 setArguments(Bundle) 方法,然后在 Fragment 中调用 getArguments() 来获取参数。
在 AS 中,创建新的 Fragment 时,会有模板给出,如下图。完成创建的代码中 TODO 提示的相关修改,就能得到一个官方推荐的碎片。
四、FragmentPagerAdapter 与 FragmentStatePagerAdapter
这两个类一般都用于和 ViewPager 结合使用。
不同点:
- FragmentPagerAdapter 对于不再需要的 Fragment 只是调用 destroyView 销毁视图,并不销毁实例
- FragmentStatePagerAdapter 对于不在需要的 Fragment 会调用 onDestroy 方法,销毁实例
因此对于滑动页面少的界面可以使用 FragmentPagerAdapter ,属于牺牲内存的减少运行时间的优化方式,对于页面较多的,我们使用 FragmentStatePagerAdapter 属于牺牲时间减少内存消耗的优化方式。
下面来看看滑动过程中的 logcat 的打印:
-
FragmentPagerAdapter
创建视图:
销毁视图:
-
FragmentStatePagerAdapter
创建 Fragment:
销毁 Fragment:
五、Fragment 之间的通信
现在复杂的 Activity 大多离不开多个 Fragment 组合实现,那么同个界面中 Fragment 应该如何通信呢?就目前而言,最好的办法莫过于使用 EventBus 这样的优秀开源库,对事件总线进行管理。但是如果不希望采用第三方库,那我们只能回到 Android SDK 中寻求一般的解决方法。从网上的资料来看,解决方法绕不开观察者模式,一般通过广播或是接口回调实现。
测试代码,我只做了最简单的布局逻辑,一个 Activity 中有左右两个 Fragment,两个 Fragment 中分别有一个 Button 和一个 TextView,Button 点击后会使另一个 Fragment 的 TextView 改变。
- 1、广播的实现:
- 在 Button 的点击事件中分别发送广播:
// LeftFragment Button 点击事件
public void onLeftFragmentButtonClick(View v){
Intent intent = new Intent(ACTION_LEFT);
intent.putString(INFO, "msg from left");// 传入数据
sendBroadcast(intent);
}
// RightFragment Button 点击事件
public void onRightFragmentButtonClick(View v){
Intent intent = new Intent(ACTION_RIGHT);
intent.putString(INFO, "msg from right");// 传入数据
sendBroadcast(intent);
}
- 在 Fragment 的 onCreate 方法中注册广播监听:
// LeftFragment
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
BroadcastReceiver br = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {// 处理数据
String info = intent.getStringExtra(INFO);
textView.setText(info);
}
};
lbm.registerReceiver(br,new IntentFilter(ACTION_RIGHT));
// RightFragment
LocalBroadcastManager lbm = LocalBroadcastManager.getInstance(getContext());
BroadcastReceiver br = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {// 处理数据
String info = intent.getStringExtra(INFO);
textView.setText(info);
}
};
lbm.registerReceiver(br,new IntentFilter(ACTION_LEFT));
通过这种方式,我们便可以简单地利用广播实现 Fragment 之间的通信。用这种方法实现通信的时候需要注意这样几点:
广播注册后需要在 onDestroy 中注销。
特殊场景可能会涉及广播的其他用法。
我们需要注意事件发送和广播注册的时机,例如在当前 Fragment 中点击后,弹出一个 FragmentDialog,如果需要该 Fragment
与这个 FragmentDialog 通信,那么上面的方法是无法接收到广播的,因为发送时, FragmentDialog 中的广播接收器并没有注册。这个时候我们需要发送粘滞广播。广播接收器中不要处理耗时操作,因为这样做十分容易造成 ANR。当然如果一定要这样操作,也是有办法的,这点我会在下一篇 Broadcast 总结里提到。
2、接口回调
如果我们有了一段不短的学习经历,或者有了一些开发经验,那么我们就会很自然地发现,我们在开发中几乎离不开接口回调的应用。事实上,Google 在 AS 创建的模板里也有不少接口回调的影子,比如 Fragment。
在创建 BlankFragment 的时候如果勾选了 include the interface callbacks,我们就可以看到在 onAttach 方法中,就自动把 context 对象绑定到监听器上,如果这个 Activity 没有实现这个接口会出运行时异常。我们也参考这个方式进行操作,并且将 View 的处理都放在宿主 Activity 里。
参考代码:
LeftFragment:
public class LeftFragment extends Fragment {
private OnLeftFragmentListener mListener;
private TextView mTextView;
public LeftFragment() {
// Required empty public constructor
}
public static LeftFragment newInstance() {
LeftFragment fragment = new LeftFragment();
// 传入数据
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
// 初始化数据
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_left, container, false);
mTextView = ((TextView) view.findViewById(R.id.fragment_left_text));
view.findViewById(R.id.fragment_left_btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mListener != null) {
mListener.onLeftClick("msg from left");
}
}
});
return view;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnLeftFragmentListener) {
mListener = (OnLeftFragmentListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnLeftFragmentListener {
void onLeftClick(String info);
}
public void receiveInfo(String info){
mTextView.setText(info);
}
}
由于左右碎片的代码是一样的,因此不再贴了,直接是宿主 Activity:
public class Fun3Activity extends BaseActivity
implements LeftFragment.OnLeftFragmentListener,
RightFragment.OnRightFragmentListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
protected void initViews() {
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.activity_fun3_left_container,LeftFragment.newInstance(),"LEFT");
ft.add(R.id.activity_fun3_right_container,RightFragment.newInstance(),"RIGHT");
ft.commit();
}
@Override
public int getLayoutId() {
return R.layout.activity_fun3;
}
@Override
public void onRightClick(String info) {
FragmentManager fm = getSupportFragmentManager();
LeftFragment leftFragment = (LeftFragment) fm.findFragmentByTag("LEFT");
leftFragment.receiveInfo(info);
}
@Override
public void onLeftClick(String info) {
FragmentManager fm = getSupportFragmentManager();
RightFragment rightFragment = (RightFragment) fm.findFragmentByTag("RIGHT");
rightFragment.receiveInfo(info);
}
}