8、Fragment详解:生命周期、通信、管理与实战
Fragment 这东西,说白了就是 Activity 里的“子页面”。
我刚开始学 Android 时,总觉得 Fragment 有点多余——一个 Activity 不够用吗?后来项目越做越大,才发现 Fragment 是真香。它让页面复用变得特别灵活,尤其是平板和手机适配,没有 Fragment 简直寸步难行。
8.1 Fragment 的生命周期
Fragment 的生命周期比 Activity 复杂一些。为什么?因为它依附于 Activity,Activity 活着它才能活着。
先看一张我画的流程图,帮你快速建立整体印象:
嗯,这里要注意:onCreateView() 是加载布局的地方,onActivityCreated() 是确保 Activity 已创建完成,可以安全操作 Activity 组件的地方。
核心区别:Fragment 有 onAttach() 和 onDetach(),Activity 没有。这两个方法专门处理 Fragment 与 Activity 的绑定和解绑。
8.2 Fragment 与 Activity 通信
通信这块,我见过太多新手直接写 ((MainActivity) getActivity()).xxx()。能用,但耦合太强。你想想看,如果哪天 Activity 换了,代码全得改。
我个人习惯用接口回调。Fragment 定义一个接口,Activity 去实现。这样 Fragment 完全不知道 Activity 是谁,只知道自己要调用一个接口方法。
// Fragment 中定义接口
public interface OnItemClickListener {
void onItemClick(String data);
}
private OnItemClickListener listener;
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnItemClickListener) {
listener = (OnItemClickListener) context;
}
}
// 需要通知 Activity 时
listener.onItemClick("用户点击了某个条目");
小技巧:在 onDetach() 里把 listener 置为 null,防止内存泄漏。我曾经因为忘了这一步,导致 Activity 被 Fragment 持有引用,内存泄漏查了半天。
反过来,Activity 向 Fragment 传数据,用 Bundle 或者直接调用 Fragment 的公共方法就行。
// Activity 中
Fragment fragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("key", "value");
fragment.setArguments(bundle);
// Fragment 中获取
String value = getArguments().getString("key");
8.3 FragmentManager 与 FragmentTransaction
FragmentManager 是 Fragment 的大管家。所有增删改查操作,都得通过它来调度。
FragmentTransaction 是具体执行者。每次操作都要 beginTransaction() 开启一个事务,然后 add()、replace()、remove(),最后 commit() 提交。
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.container, new MyFragment(), "TAG_MY");
ft.addToBackStack(null); // 加入返回栈
ft.commit();
注意:commit() 必须在 Activity 保存状态之前调用。如果在 onSaveInstanceState() 之后调用,会抛异常。可以用 commitAllowingStateLoss() 绕过,但我不建议滥用——数据丢失了别怪我没提醒。
我遇到过一个问题:连续快速点击按钮,导致 Fragment 重复添加。解决方案很简单——在 commit() 之前检查 isAdded() 或者用 popBackStackImmediate() 清理。
8.4 ViewPager + Fragment 实战
ViewPager 搭配 Fragment,是 App 首页最常用的组合。说白了就是左右滑动切换页面,每个页面是一个 Fragment。
核心适配器是 FragmentPagerAdapter 或 FragmentStatePagerAdapter。区别在于:
| 适配器 | 特点 | 适用场景 |
|---|---|---|
| FragmentPagerAdapter | Fragment 常驻内存,不会销毁 | 页面较少(3-5个),切换频繁 |
| FragmentStatePagerAdapter | 不可见 Fragment 会被销毁,只保存状态 | 页面较多,节省内存 |
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private List<Fragment> fragments;
public MyPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
this.fragments = fragments;
}
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
// 使用
ViewPager viewPager = findViewById(R.id.viewPager);
viewPager.setAdapter(new MyPagerAdapter(getSupportFragmentManager(), fragmentList));
避坑指南:使用 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT 这个参数。旧版默认是 BEHAVIOR_SET_USER_VISIBLE_HINT,会导致 Fragment 的 onResume() 在不可见时也被调用。我踩过这个坑,日志里一堆奇怪的生命周期问题。
实战中,我通常还会配合 TabLayout 一起用:
TabLayout tabLayout = findViewById(R.id.tabLayout);
tabLayout.setupWithViewPager(viewPager);
// 设置 Tab 标题
viewPager.addOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
// 页面切换时的逻辑
}
});
嗯,到这里 Fragment 的核心内容基本讲完了。生命周期是基础,通信是桥梁,FragmentManager 是调度中心,ViewPager 是实战利器。把这四个点吃透,Fragment 这块你就拿下了。
一句话总结:Fragment 不是 Activity 的替代品,而是 Activity 的得力助手。用好了,代码复用率翻倍;用不好,生命周期能让你怀疑人生。