目录
前言:为什么 Fragment 是 Android 页面开发的 “模块化神器”?
二、Fragment 生命周期:与 Activity 的联动逻辑(实战解析)
示例 1:Fragment 与 Activity 生命周期联动日志
方式 1:onSaveInstanceState ()(轻量级数据)
三、Fragment 的两种加载方式:静态加载 vs 动态加载
核心 API:FragmentManager 与 FragmentTransaction
4. 隐藏 / 显示 Fragment(hide/show)
5. 回退 Fragment(addToBackStack)
4.1 场景 1:Fragment → Activity(数据传递)
示例 6:Fragment 通过接口回调向 Activity 传值
4.2 场景 2:Activity → Fragment(数据传递)
方案 1:Bundle 传参(初始化 Fragment 时)
4.3 场景 3:Fragment → Fragment(数据传递)
方案 1:Activity 中转(通过接口回调 + Bundle)
五、Fragment 进阶用法:ViewPager2 结合、懒加载、特殊 Fragment
5.1 ViewPager2 + Fragment(滑动切换,主流组合)
示例 8:ViewPager2 + Fragment 实现滑动切换
示例 9:ViewPager2 + Fragment 实现懒加载
5.3 特殊 Fragment:DialogFragment、BottomSheetFragment
1. DialogFragment(弹窗 Fragment,替代 Dialog)
2. BottomSheetFragment(底部弹窗 Fragment)
6.2 坑点 2:Fragment 获取 Activity 为 null
6.3 坑点 3:FragmentTransaction.commit () 报错(状态非法)
6.4 坑点 4:ViewPager2 预加载导致懒加载失效

class 卑微码农:
def __init__(self):
self.技能 = ['能读懂十年前祖传代码', '擅长用Ctrl+C/V搭建世界', '信奉"能跑就别动"的玄学']
self.发量 = 100 # 初始发量
self.咖啡因耐受度 = '极限'
def 修Bug(self, bug):
try:
# 试图用玄学解决问题
if bug.严重程度 == '离谱':
print("这一定是环境问题!")
else:
print("让我看看是谁又没写注释...哦,是我自己。")
except Exception as e:
# 如果try块都救不了,那就...
print("重启一下试试?")
self.发量 -= 1 # 每解决一个bug,头发-1
# 实例化一个我
我 = 卑微码农()
前言:为什么 Fragment 是 Android 页面开发的 “模块化神器”?
刚入门 Android 时,曾用 Activity 堆砌所有页面 —— 一个 APP 写了十几个 Activity,跳转逻辑混乱,相同 UI 组件重复编写,屏幕旋转后数据还容易丢失。直到接触了 Fragment,才发现它的核心价值:把页面拆分成独立的 “模块” ,可复用、可组合、可管理,彻底解决了 Activity 臃肿、耦合度高的问题。

Fragment 就像 “迷你版 Activity”,有自己的布局和生命周期,却必须依附于 Activity 存在。它能让你在一个 Activity 中切换多个 UI 模块(如首页的 “首页 / 发现 / 我的”),也能在不同 Activity 中复用同一个模块(如商品详情页在购物 APP 和电商小程序中复用)。
但很多开发者用不好 Fragment,要么遇到 “切换时 Fragment 重叠”“生命周期混乱”“通信失败” 的问题,要么不知道什么时候该用 Fragment、什么时候该用 Activity。其实 Fragment 的逻辑不复杂,关键是掌握 “生命周期联动”“动态操作”“组件通信” 这三大核心。
本文会把 Fragment 的基础概念、生命周期、创建使用、通信方式、进阶用法、避坑指南全讲透。全文包含 10 个完整可运行的原创示例,从基础入门到进阶实战,新手能直接上手,进阶开发者能夯实基础、避开坑点。
建议先收藏,再跟着示例一步步实操,遇到问题可以在评论区交流~
一、Fragment 基础认知:到底什么是碎片?

1.1 一句话搞懂 Fragment 的核心作用
Fragment(简称 “碎片”)是 Android 3.0(API 11)引入的组件,核心作用是实现页面模块化拆分,支持 UI 复用与灵活组合。
简单说:Fragment 是 “可嵌入 Activity 的独立 UI 模块”—— 它有自己的布局(layout)、生命周期回调,能处理用户交互(如按钮点击),但不能单独存在,必须依附于 Activity。就像搭积木,Activity 是 “积木底板”,Fragment 是 “不同形状的积木”,可以按需组合、替换,搭建出多样化的页面。
举个实际场景:某信首页的 “某信 / 通讯录 / 发现 / 我” 四个模块,每个都是一个 Fragment,通过底部 Tab 切换时,Activity 不变,只替换中间的 Fragment,既高效又灵活。
1.2 必须澄清的 3 个常见误解
- 误解 1:Fragment 是 Activity 的 “子页面”?—— 错!Fragment 是独立的模块,与 Activity 是 “合作关系” 而非 “父子关系”,可在多个 Activity 中复用;
- 误解 2:Fragment 比 Activity 轻量,所以尽量多用?—— 错!Fragment 的生命周期更复杂(与 Activity 联动),滥用会导致逻辑混乱,需根据场景选择;
- 误解 3:Fragment 只能在手机上用?—— 错!Fragment 最初是为平板设计的(适配大屏幕多窗口),现在手机端模块化开发中更常用。
1.3 Fragment 与 Activity 的核心区别
| 特性 | Fragment | Activity |
|---|---|---|
| 独立存在性 | 不能单独存在,必须依附 Activity | 可单独存在,是四大组件之一 |
| 布局加载 | 通过onCreateView()加载布局 | 通过setContentView()加载布局 |
| 生命周期 | 受 Activity 生命周期联动影响 | 独立生命周期,受系统直接管理 |
| 启动方式 | 不能直接启动,需通过 Activity 加载 | 可通过 Intent 直接启动 |
| 核心作用 | 页面模块化拆分、UI 复用 | 作为页面容器,管理 Fragment 和组件 |
| 配置变更(如旋转) | 默认随 Activity 重建(可通过 ViewModel 保存数据) | 默认重建(可配置configChanges避免) |
1.4 Fragment 的典型适用场景
- 多 Tab 页面:如首页底部 Tab 切换(微信、抖音、淘宝);
- UI 复用场景:如商品详情页、评论列表在多个 Activity 中复用;
- 大屏幕适配:如平板的 “列表 + 详情” 双栏布局(左侧 Fragment 显示列表,右侧显示详情);
- 动态页面组合:如根据用户权限显示不同的 Fragment(登录后显示个人中心,未登录显示登录 Fragment);
- 复杂页面拆分:如一个包含 “表单 + 列表 + 图表” 的复杂页面,拆分成 3 个 Fragment 分别管理,降低耦合。
二、Fragment 生命周期:与 Activity 的联动逻辑(实战解析)

Fragment 的生命周期比 Activity 复杂,核心原因是它的生命周期会与依附的 Activity “联动”——Activity 的状态变化会直接影响 Fragment 的状态。这是掌握 Fragment 的关键,也是最容易踩坑的地方。
2.1 Fragment 的 11 个核心生命周期回调
Fragment 的生命周期回调比 Activity 多,核心有 11 个,每个都与 Activity 的生命周期对应,我们结合实际场景理解:

| 方法名 | 调用时机 | 核心作用 | 与 Activity 的联动关系 |
|---|---|---|---|
| onAttach(Context) | Fragment 与 Activity 关联时(仅调用 1 次) | 获取 Activity 引用,初始化全局数据 | 早于 Activity 的 onCreate () |
| onCreate(Bundle) | Fragment 创建时(仅调用 1 次) | 初始化非 UI 数据(如 ViewModel、数据请求) | 与 Activity 的 onCreate () 同步或稍晚 |
| onCreateView() | Fragment 创建视图时(加载布局) | 加载 Fragment 的布局(inflater.inflate ()) | 在 Activity 的 onCreate () 之后、onStart () 之前 |
| onViewCreated() | Fragment 视图创建完成后 | 绑定控件、设置监听(如按钮点击) | 紧跟 onCreateView () 之后 |
| onActivityCreated() | 依附的 Activity 创建完成后(API 28 后废弃) | 访问 Activity 的控件或数据(已过时,用 onViewCreated 替代) | Activity 的 onCreate () 执行完成后 |
| onStart() | Fragment 可见时 | 启动动画、注册非前台监听器 | 与 Activity 的 onStart () 同步 |
| onResume() | Fragment 可见且可交互时 | 启动前台服务、恢复播放 | 与 Activity 的 onResume () 同步 |
| onPause() | Fragment 即将失去焦点时 | 暂停动画、保存临时数据 | 与 Activity 的 onPause () 同步 |
| onStop() | Fragment 完全不可见时 | 释放大内存资源、注销监听器 | 与 Activity 的 onStop () 同步 |
| onDestroyView() | Fragment 视图销毁时 | 解绑控件引用、释放视图相关资源 | 在 Activity 的 onDestroy () 之前 |
| onDestroy() | Fragment 销毁时(仅调用 1 次) | 彻底释放非视图资源(如 ViewModel、异步任务) | 在 onDestroyView () 之后 |
| onDetach() | Fragment 与 Activity 解除关联时(仅调用 1 次) | 清空 Activity 引用(避免内存泄漏) | 晚于 Fragment 的 onDestroy (),早于 Activity 的 onDestroy () |
2.2 生命周期联动流程:用代码实测
光看表格不够直观,我们通过一个示例,打印 Fragment 和 Activity 的生命周期回调,观察它们的联动关系。
示例 1:Fragment 与 Activity 生命周期联动日志
- 创建宿主 Activity(FragmentHostActivity):
public class FragmentHostActivity extends AppCompatActivity {
private static final String TAG = "LifecycleTest";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_host);
Log.d(TAG, "Activity onCreate");
// 加载Fragment(静态加载,后续讲动态加载)
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new TestFragment())
.commit();
}
}
@Override
protected void onStart() {
super.onStart();
Log.d(TAG, "Activity onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "Activity onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.d(TAG, "Activity onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.d(TAG, "Activity onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d(TAG, "Activity onDestroy");
}
}
- 创建 TestFragment:
public class TestFragment extends Fragment {
private static final String TAG = "LifecycleTest";
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
Log.d(TAG, "Fragment onAttach");
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "Fragment onCreate");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "Fragment onCreateView");
// 加载Fragment布局(简单布局:仅一个TextView)
return inflater.inflate(R.layout.fragment_test, container, false);
}
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d(TAG, "Fragment onViewCreated");
// 绑定控件
TextView tv = view.findViewById(R.id.tv_fragment);
tv.setText("测试Fragment生命周期");
}
@Override
public void onStart() {
super.onStart();
Log.d(TAG, "Fragment onStart");
}
@Override
public void onResume() {
super.onResume();
Log.d(TAG, "Fragment onResume");
}
@Override
public void onPause() {
super.onPause();
Log.d(TAG, "Fragment onPause");
}
@Override
public void onStop() {
super.onStop();
Log.d(TAG, "Fragment onStop");
}
@Override
public void onDestroyView() {
super.onDestroyView();
Log.d(TAG, "Fragment onDestroyView");
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG, "Fragment onDestroy");
}
@Override
public void onDetach() {
super.onDetach();
Log.d(TAG, "Fragment onDetach");
}
}
- 对应的布局文件:
- activity_fragment_host.xml(Activity 布局,包含 Fragment 容器):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
- fragment_test.xml(Fragment 布局):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center">
<TextView
android:id="@+id/tv_fragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="20sp" />
</LinearLayout>
- 运行测试,观察 Logcat 日志(首次启动 Activity):
D/LifecycleTest: Fragment onAttach
D/LifecycleTest: Activity onCreate
D/LifecycleTest: Fragment onCreate
D/LifecycleTest: Fragment onCreateView
D/LifecycleTest: Fragment onViewCreated
D/LifecycleTest: Activity onStart
D/LifecycleTest: Fragment onStart
D/LifecycleTest: Activity onResume
D/LifecycleTest: Fragment onResume
- 关键场景的生命周期流程:
- 场景 1:按 Home 键返回桌面(Activity 进入后台):
D/LifecycleTest: Activity onPause
D/LifecycleTest: Fragment onPause
D/LifecycleTest: Activity onStop
D/LifecycleTest: Fragment onStop
- 场景 2:从桌面重新打开应用(Activity 恢复前台):
D/LifecycleTest: Activity onStart
D/LifecycleTest: Fragment onStart
D/LifecycleTest: Activity onResume
D/LifecycleTest: Fragment onResume
- 场景 3:按返回键退出 Activity(Fragment 与 Activity 销毁):
D/LifecycleTest: Activity onPause
D/LifecycleTest: Fragment onPause
D/LifecycleTest: Activity onStop
D/LifecycleTest: Fragment onStop
D/LifecycleTest: Fragment onDestroyView
D/LifecycleTest: Fragment onDestroy
D/LifecycleTest: Fragment onDetach
D/LifecycleTest: Activity onDestroy
- 场景 4:屏幕旋转(Activity 与 Fragment 默认重建):
// 销毁流程
D/LifecycleTest: Activity onPause
D/LifecycleTest: Fragment onPause
D/LifecycleTest: Activity onStop
D/LifecycleTest: Fragment onStop
D/LifecycleTest: Fragment onDestroyView
D/LifecycleTest: Fragment onDestroy
D/LifecycleTest: Fragment onDetach
D/LifecycleTest: Activity onDestroy
// 重建流程(与首次启动一致)
D/LifecycleTest: Fragment onAttach
D/LifecycleTest: Activity onCreate
D/LifecycleTest: Fragment onCreate
D/LifecycleTest: Fragment onCreateView
D/LifecycleTest: Fragment onViewCreated
...
2.3 生命周期核心原则:“资源对称” 与 “状态保存”
1. 资源对称原则
与 Activity 类似,Fragment 的资源申请与释放需遵循 “对称原则”,避免内存泄漏:
- onAttach ():获取 Activity 引用、初始化全局数据(如 SP);
- onCreate ():初始化非视图资源(如 ViewModel、Repository);
- onViewCreated ():绑定控件、设置监听、初始化视图相关资源(如 RecyclerView 适配器);
- onDestroyView ():解绑控件引用、释放视图资源(如 RecyclerView、动画);
- onDetach ():清空 Activity 引用(避免内存泄漏)。
反例:在 onCreateView () 中注册广播接收器,却未在 onDestroyView () 中注销,会导致 Fragment 视图销毁后仍持有广播引用,造成内存泄漏。
2. 状态保存:避免配置变更后数据丢失
屏幕旋转等配置变更时,Fragment 会随 Activity 重建,临时数据(如 EditText 输入内容、列表滚动位置)会丢失,需通过以下两种方式保存:
方式 1:onSaveInstanceState ()(轻量级数据)
Fragment 的onSaveInstanceState(Bundle outState)方法会在重建前调用,可保存简单数据:
@Override
public void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
// 保存EditText输入内容
EditText etInput = getView().findViewById(R.id.et_input);
outState.putString("input_text", etInput.getText().toString());
}
// 在onViewCreated中恢复数据
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
EditText etInput = view.findViewById(R.id.et_input);
if (savedInstanceState != null) {
String inputText = savedInstanceState.getString("input_text", "");
etInput.setText(inputText);
}
}
方式 2:ViewModel(重量级数据,推荐)
ViewModel 的生命周期独立于 Activity 和 Fragment 的重建,适合保存复杂数据(如列表数据、网络请求结果):
// 1. 创建ViewModel
public class MyViewModel extends ViewModel {
private MutableLiveData<List<String>> dataList = new MutableLiveData<>();
public LiveData<List<String>> getDataList() {
return dataList;
}
public void setDataList(List<String> list) {
dataList.setValue(list);
}
}
// 2. 在Fragment中使用ViewModel
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// 获取ViewModel(与Activity共享,确保重建后数据不丢失)
MyViewModel viewModel = new ViewModelProvider(requireActivity()).get(MyViewModel.class);
// 观察数据变化
viewModel.getDataList().observe(getViewLifecycleOwner(), list -> {
// 刷新UI
});
}
三、Fragment 的两种加载方式:静态加载 vs 动态加载

Fragment 的加载方式分为静态和动态两种,静态简单但不灵活,动态是开发中的主流方式,支持灵活切换、替换 Fragment。
3.1 静态加载(声明式加载,简单场景)
静态加载是在 Activity 的布局文件中直接声明 Fragment,适用于无需动态切换的场景(如固定的双栏布局)。
示例 2:静态加载 Fragment
- 编写 Fragment(StaticFragment):
public class StaticFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_static, container, false);
}
}
- 在 Activity 布局中声明 Fragment:
<!-- activity_static_host.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- 左侧Fragment(占1/3宽度) -->
<fragment
android:id="@+id/fragment_left"
android:name="com.example.fragment.StaticFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<!-- 右侧Fragment(占2/3宽度) -->
<fragment
android:id="@+id/fragment_right"
android:name="com.example.fragment.StaticFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
- 宿主 Activity(无需额外代码,布局加载时自动初始化 Fragment):
public class StaticHostActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_static_host);
}
}
静态加载的优缺点:
- 优点:实现简单,无需手动管理 Fragment 的创建和生命周期;
- 缺点:不支持动态切换、替换 Fragment,灵活性极低;
- 适用场景:平板双栏布局、固定不变的 UI 模块。
3.2 动态加载(代码式加载,主流方式)
动态加载是通过FragmentManager和FragmentTransaction在代码中动态添加、替换、移除 Fragment,支持灵活的页面切换(如底部 Tab、侧边栏)。
核心 API:FragmentManager 与 FragmentTransaction
- FragmentManager:Fragment 的 “管理器”,负责 Fragment 的添加、查找、回退等操作,通过
getSupportFragmentManager()(AndroidX)获取; - FragmentTransaction:Fragment 的 “事务”,封装了 Fragment 的一系列操作(add、replace、remove、hide、show),需通过
beginTransaction()创建,最后调用commit()提交。
动态加载的 5 个核心操作
1. 添加 Fragment(add)
将 Fragment 添加到 Activity 的容器中,可添加多个 Fragment 叠加显示:
// 示例3:动态添加Fragment
private void addFragment() {
// 1. 获取FragmentManager
FragmentManager manager = getSupportFragmentManager();
// 2. 开启事务
FragmentTransaction transaction = manager.beginTransaction();
// 3. 创建Fragment实例
DynamicFragment fragment = new DynamicFragment();
// 4. 添加Fragment到容器(参数:容器ID、Fragment实例、标签(用于查找))
transaction.add(R.id.fragment_container, fragment, "dynamic_fragment");
// 5. 提交事务
transaction.commit();
}
2. 替换 Fragment(replace)
移除容器中所有已添加的 Fragment,再添加新的 Fragment(常用语 Tab 切换):
// 示例4:动态替换Fragment(底部Tab切换核心)
private void replaceFragment(Fragment fragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
// 替换容器中的Fragment(参数:容器ID、新Fragment)
transaction.replace(R.id.fragment_container, fragment);
// 可选:添加到回退栈(按返回键可回退到上一个Fragment)
transaction.addToBackStack(null);
transaction.commit();
}
// 调用示例(切换到首页Fragment)
findViewById(R.id.tab_home).setOnClickListener(v -> {
replaceFragment(new HomeFragment());
});
3. 移除 Fragment(remove)
从容器中移除指定的 Fragment(移除后会执行 onDestroyView→onDestroy→onDetach):
private void removeFragment() {
FragmentManager manager = getSupportFragmentManager();
// 查找已添加的Fragment(通过标签)
DynamicFragment fragment = (DynamicFragment) manager.findFragmentByTag("dynamic_fragment");
if (fragment != null) {
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(fragment);
transaction.commit();
}
}
4. 隐藏 / 显示 Fragment(hide/show)
隐藏 Fragment(不销毁视图,仅不可见),显示已隐藏的 Fragment(适合频繁切换,避免重建):
// 示例5:隐藏/显示Fragment(比replace更高效)
private void hideAndShowFragment() {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
// 隐藏首页Fragment
transaction.hide(homeFragment);
// 显示发现Fragment
transaction.show(discoverFragment);
transaction.commit();
}
5. 回退 Fragment(addToBackStack)
将事务添加到回退栈,按返回键可回退到上一个 Fragment 状态:
// 添加到回退栈(参数:事务名称,可为null)
transaction.addToBackStack("replace_home");
完整示例:底部 Tab 切换(3 个 Fragment)
- Activity 布局(包含底部 Tab 和 Fragment 容器):
<!-- activity_tab_host.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Fragment容器 -->
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<!-- 底部Tab(3个按钮) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<Button
android:id="@+id/tab_home"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="首页" />
<Button
android:id="@+id/tab_discover"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="发现" />
<Button
android:id="@+id/tab_mine"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:text="我的" />
</LinearLayout>
</LinearLayout>
- 3 个简单的 Fragment(HomeFragment、DiscoverFragment、MineFragment,结构一致):
public class HomeFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
TextView tv = view.findViewById(R.id.tv_home);
tv.setText("首页Fragment");
return view;
}
}
- 宿主 Activity(实现 Tab 切换逻辑):
public class TabHostActivity extends AppCompatActivity {
private HomeFragment homeFragment;
private DiscoverFragment discoverFragment;
private MineFragment mineFragment;
private Fragment currentFragment; // 当前显示的Fragment
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_tab_host);
// 初始化3个Fragment
homeFragment = new HomeFragment();
discoverFragment = new DiscoverFragment();
mineFragment = new MineFragment();
// 默认显示首页Fragment
switchFragment(homeFragment);
// 底部Tab点击事件
findViewById(R.id.tab_home).setOnClickListener(v -> switchFragment(homeFragment));
findViewById(R.id.tab_discover).setOnClickListener(v -> switchFragment(discoverFragment));
findViewById(R.id.tab_mine).setOnClickListener(v -> switchFragment(mineFragment));
}
// 切换Fragment(核心方法:hide当前,show目标)
private void switchFragment(Fragment targetFragment) {
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction transaction = manager.beginTransaction();
// 如果目标Fragment未添加,先添加
if (!targetFragment.isAdded()) {
transaction.add(R.id.fragment_container, targetFragment);
}
// 隐藏当前Fragment,显示目标Fragment
if (currentFragment != null) {
transaction.hide(currentFragment);
}
transaction.show(targetFragment);
// 更新当前Fragment
currentFragment = targetFragment;
// 提交事务
transaction.commit();
}
}
关键说明:
- 用 hide/show 切换比 replace 更高效,因为 Fragment 的视图不会销毁,避免重复 inflate 布局;
- 初始化 Fragment 时只创建一次实例,避免频繁创建导致的性能问题;
- 无需添加到回退栈(底部 Tab 切换通常不需要回退),如需回退可添加
addToBackStack()。
四、Fragment 通信:组件间数据传递全攻略

Fragment 的通信是开发中的核心难点,常见场景有 3 种:Fragment 与 Activity、Fragment 与 Fragment、Fragment 与 ViewModel,每种场景都有对应的最优方案。
4.1 场景 1:Fragment → Activity(数据传递)
Fragment 向 Activity 传递数据,推荐用 “接口回调”(简单场景)或 “ViewModel”(复杂场景)。
方案 1:接口回调(推荐,低耦合)
示例 6:Fragment 通过接口回调向 Activity 传值
- 在 Fragment 中定义回调接口:
public class DataFragment extends Fragment {
// 回调接口(定义要传递的数据类型)
public interface OnDataSendListener {
void onDataSend(String data); // 传递字符串数据
}
private OnDataSendListener mListener;
// Fragment与Activity关联时,获取接口实例
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
// 验证Activity是否实现了接口
if (context instanceof OnDataSendListener) {
mListener = (OnDataSendListener) context;
} else {
throw new IllegalStateException("Activity必须实现OnDataSendListener接口");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_data, container, false);
// 按钮点击:传递数据给Activity
view.findViewById(R.id.btn_send).setOnClickListener(v -> {
if (mListener != null) {
mListener.onDataSend("Fragment传递的数据");
}
});
return view;
}
// 解除关联时,清空接口引用(避免内存泄漏)
@Override
public void onDetach() {
super.onDetach();
mListener = null;
}
}
- Activity 实现接口,接收数据:
public class DataHostActivity extends AppCompatActivity implements DataFragment.OnDataSendListener {
private TextView tvReceived;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_host);
tvReceived = findViewById(R.id.tv_received);
// 加载DataFragment
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new DataFragment())
.commit();
}
// 实现接口方法,接收Fragment传递的数据
@Override
public void onDataSend(String data) {
tvReceived.setText("收到Fragment数据:" + data);
}
}
方案 2:ViewModel(复杂数据,跨组件共享)
ViewModel 可在 Activity 和多个 Fragment 之间共享数据,适合复杂场景(如多个 Fragment 向 Activity 传值):
// 1. 创建共享ViewModel
public class SharedViewModel extends ViewModel {
private MutableLiveData<String> sharedData = new MutableLiveData<>();
public void setSharedData(String data) {
sharedData.setValue(data);
}
public LiveData<String> getSharedData() {
return sharedData;
}
}
// 2. Fragment中发送数据
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
// 发送数据
view.findViewById(R.id.btn_send).setOnClickListener(v -> {
viewModel.setSharedData("Fragment传递的数据");
});
}
// 3. Activity中接收数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_data_host);
TextView tvReceived = findViewById(R.id.tv_received);
SharedViewModel viewModel = new ViewModelProvider(this).get(SharedViewModel.class);
viewModel.getSharedData().observe(this, data -> {
tvReceived.setText("收到Fragment数据:" + data);
});
}
4.2 场景 2:Activity → Fragment(数据传递)
Activity 向 Fragment 传递数据,推荐用 “Bundle 传参”(初始化时)或 “ViewModel”(动态更新)。
方案 1:Bundle 传参(初始化 Fragment 时)
这是最常用的方式,通过setArguments(Bundle)传递数据,Fragment 在onCreate()或onViewCreated()中接收:
// 示例7:Activity向Fragment传递数据
public class SendToFragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_send_to_fragment);
// 1. 创建Bundle,存储数据
Bundle bundle = new Bundle();
bundle.putString("name", "张三");
bundle.putInt("age", 25);
bundle.putBoolean("isStudent", true);
// 2. 创建Fragment,设置Arguments
ReceiveFragment fragment = new ReceiveFragment();
fragment.setArguments(bundle);
// 3. 加载Fragment
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
// Fragment接收数据
public class ReceiveFragment extends Fragment {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 接收数据(getArguments()获取Bundle)
if (getArguments() != null) {
String name = getArguments().getString("name");
int age = getArguments().getInt("age");
boolean isStudent = getArguments().getBoolean("isStudent");
Log.d("FragmentTest", "姓名:" + name + ",年龄:" + age + ",是否学生:" + isStudent);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_receive, container, false);
// 显示数据
TextView tv = view.findViewById(R.id.tv_data);
if (getArguments() != null) {
String name = getArguments().getString("name");
tv.setText("收到Activity数据:" + name);
}
return view;
}
}
关键注意事项:
- 传递的数据必须是可序列化的(基本数据类型、String、Parcelable、Serializable);
- 不要直接通过 Fragment 的构造方法传参(Activity 重建时,Fragment 会通过无参构造方法重建,参数会丢失);
- 推荐用静态工厂方法创建 Fragment(封装传参逻辑):
// Fragment中添加静态工厂方法(推荐)
public static ReceiveFragment newInstance(String name, int age) {
ReceiveFragment fragment = new ReceiveFragment();
Bundle bundle = new Bundle();
bundle.putString("name", name);
bundle.putInt("age", age);
fragment.setArguments(bundle);
return fragment;
}
// Activity中创建Fragment
ReceiveFragment fragment = ReceiveFragment.newInstance("张三", 25);
方案 2:ViewModel(动态更新数据)
如果需要在 Fragment 创建后,Activity 动态向其传递数据(如用户操作后更新),推荐用 ViewModel:
// 1. 共享ViewModel(与Activity和Fragment共享)
public class UpdateViewModel extends ViewModel {
private MutableLiveData<String> updateData = new MutableLiveData<>();
public void updateData(String data) {
updateData.setValue(data);
}
public LiveData<String> getUpdateData() {
return updateData;
}
}
// 2. Activity中动态更新数据
public class DynamicUpdateActivity extends AppCompatActivity {
private UpdateViewModel viewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic_update);
viewModel = new ViewModelProvider(this).get(UpdateViewModel.class);
// 加载Fragment
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, new UpdateFragment())
.commit();
// 按钮点击:动态更新数据
findViewById(R.id.btn_update).setOnClickListener(v -> {
viewModel.updateData("Activity动态更新的数据:" + System.currentTimeMillis());
});
}
}
// 3. Fragment中观察数据变化
public class UpdateFragment extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_update, container, false);
TextView tvUpdate = view.findViewById(R.id.tv_update);
UpdateViewModel viewModel = new ViewModelProvider(requireActivity()).get(UpdateViewModel.class);
// 观察数据变化,自动更新UI
viewModel.getUpdateData().observe(getViewLifecycleOwner(), data -> {
tvUpdate.setText(data);
});
return view;
}
}
4.3 场景 3:Fragment → Fragment(数据传递)
两个 Fragment 之间传递数据,禁止直接持有对方引用(会导致强耦合和内存泄漏),推荐通过 “Activity 中转” 或 “ViewModel 共享”。
方案 1:Activity 中转(通过接口回调 + Bundle)
流程:Fragment A → Activity → Fragment B
// 1. Fragment A定义回调接口,向Activity传值
public class FragmentA extends Fragment {
public interface OnADataSendListener {
void onADataSend(String data);
}
private OnADataSendListener mListener;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
mListener = (OnADataSendListener) context;
}
// 发送数据到Activity
private void sendDataToActivity() {
if (mListener != null) {
mListener.onADataSend("Fragment A的数据");
}
}
}
// 2. Activity实现接口,接收数据并传递给Fragment B
public class FragmentHostActivity extends AppCompatActivity implements FragmentA.OnADataSendListener {
private FragmentB fragmentB;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment_host);
// 初始化两个Fragment
FragmentA fragmentA = new FragmentA();
fragmentB = new FragmentB();
// 加载两个Fragment
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container_a, fragmentA)
.add(R.id.fragment_container_b, fragmentB)
.commit();
}
// 接收Fragment A的数据,传递给Fragment B
@Override
public void onADataSend(String data) {
// Fragment B通过方法接收数据
fragmentB.receiveDataFromA(data);
}
}
// 3. Fragment B提供方法,接收数据
public class FragmentB extends Fragment {
private TextView tvData;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_b, container, false);
tvData = view.findViewById(R.id.tv_data);
return view;
}
// 提供公共方法,接收Fragment A的数据
public void receiveDataFromA(String data) {
tvData.setText("收到Fragment A的数据:" + data);
}
}
方案 2:ViewModel 共享(推荐,低耦合)
两个 Fragment 通过 Activity 共享同一个 ViewModel,直接传递数据,无需 Activity 中转:
// 1. 共享ViewModel
public class FragmentShareViewModel extends ViewModel {
private MutableLiveData<String> shareData = new MutableLiveData<>();
public void setShareData(String data) {
shareData.setValue(data);
}
public LiveData<String> getShareData() {
return shareData;
}
}
// 2. Fragment A发送数据
public class FragmentA extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_a, container, false);
// 获取共享ViewModel(通过requireActivity(),与Activity共享)
FragmentShareViewModel viewModel = new ViewModelProvider(requireActivity()).get(FragmentShareViewModel.class);
// 发送数据
view.findViewById(R.id.btn_send).setOnClickListener(v -> {
viewModel.setShareData("Fragment A的数据");
});
return view;
}
}
// 3. Fragment B接收数据
public class FragmentB extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_b, container, false);
TextView tvData = view.findViewById(R.id.tv_data);
// 获取共享ViewModel
FragmentShareViewModel viewModel = new ViewModelProvider(requireActivity()).get(FragmentShareViewModel.class);
// 观察数据变化
viewModel.getShareData().observe(getViewLifecycleOwner(), data -> {
tvData.setText("收到Fragment A的数据:" + data);
});
return view;
}
}
4.4 3 种通信方式对比(选择指南)
| 通信场景 | 推荐方式 | 优点 | 缺点 |
|---|---|---|---|
| Fragment → Activity | 接口回调 / ViewModel | 低耦合、灵活 | 接口回调需手动管理引用 |
| Activity → Fragment | Bundle 传参 / ViewModel | Bundle 简单直接,ViewModel 支持动态更新 | Bundle 仅支持初始化传参 |
| Fragment → Fragment | ViewModel 共享 | 低耦合、无需 Activity 中转 | 需依赖 ViewModel 组件 |
五、Fragment 进阶用法:ViewPager2 结合、懒加载、特殊 Fragment

5.1 ViewPager2 + Fragment(滑动切换,主流组合)
ViewPager2 是 AndroidX 提供的滑动容器,与 Fragment 结合可实现 “滑动切换 + 底部 Tab” 的经典布局(如抖音、微信公众号),支持预加载、无限滑动等功能。
示例 8:ViewPager2 + Fragment 实现滑动切换
- 添加 ViewPager2 依赖(AndroidX):
implementation 'androidx.viewpager2:viewpager2:1.0.0'
- Activity 布局(ViewPager2 + 底部 Tab):
<!-- activity_viewpager2_host.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- ViewPager2容器 -->
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/view_pager2"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<!-- 底部Tab(3个TextView) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tab1"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="页面1"
android:textSize="16sp" />
<TextView
android:id="@+id/tab2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="页面2"
android:textSize="16sp" />
<TextView
android:id="@+id/tab3"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="页面3"
android:textSize="16sp" />
</LinearLayout>
</LinearLayout>
- 创建 Fragment 适配器(FragmentStateAdapter):
// ViewPager2的Fragment适配器(替代旧版FragmentPagerAdapter)
public class MyFragmentAdapter extends FragmentStateAdapter {
// Fragment列表
private List<Fragment> fragmentList;
public MyFragmentAdapter(@NonNull FragmentActivity fragmentActivity, List<Fragment> fragments) {
super(fragmentActivity);
this.fragmentList = fragments;
}
// 返回Fragment实例
@NonNull
@Override
public Fragment createFragment(int position) {
return fragmentList.get(position);
}
// 返回Fragment数量
@Override
public int getItemCount() {
return fragmentList.size();
}
}
- 宿主 Activity(绑定 ViewPager2 与 Tab):
public class ViewPager2HostActivity extends AppCompatActivity {
private ViewPager2 viewPager2;
private TextView tab1, tab2, tab3;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_viewpager2_host);
// 初始化ViewPager2和Tab
viewPager2 = findViewById(R.id.view_pager2);
tab1 = findViewById(R.id.tab1);
tab2 = findViewById(R.id.tab2);
tab3 = findViewById(R.id.tab3);
// 初始化Fragment列表
List<Fragment> fragments = new ArrayList<>();
fragments.add(new Page1Fragment());
fragments.add(new Page2Fragment());
fragments.add(new Page3Fragment());
// 设置适配器
MyFragmentAdapter adapter = new MyFragmentAdapter(this, fragments);
viewPager2.setAdapter(adapter);
// Tab点击事件:切换ViewPager2页面
tab1.setOnClickListener(v -> viewPager2.setCurrentItem(0));
tab2.setOnClickListener(v -> viewPager2.setCurrentItem(1));
tab3.setOnClickListener(v -> viewPager2.setCurrentItem(2));
// ViewPager2滑动事件:同步更新Tab状态
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
// 重置所有Tab颜色
tab1.setTextColor(Color.BLACK);
tab2.setTextColor(Color.BLACK);
tab3.setTextColor(Color.BLACK);
// 选中Tab设置红色
switch (position) {
case 0:
tab1.setTextColor(Color.RED);
break;
case 1:
tab2.setTextColor(Color.RED);
break;
case 2:
tab3.setTextColor(Color.RED);
break;
}
}
});
// 默认选中第一个Tab
tab1.setTextColor(Color.RED);
}
}
ViewPager2 核心特性:
- 支持横向 / 纵向滑动(通过
setOrientation()设置); - 预加载机制:默认预加载相邻 1 个 Fragment,可通过
setOffscreenPageLimit()调整; - 懒加载:需结合
FragmentTransaction.setMaxLifecycle()实现(下文讲解); - 适配 RecyclerView:内部基于 RecyclerView 实现,支持 RecyclerView 的所有特性。
5.2 Fragment 懒加载(按需加载数据,优化性能)
ViewPager2 默认会预加载相邻的 Fragment,导致未显示的 Fragment 也会执行onCreateView()和onResume(),浪费资源(如提前请求网络)。懒加载可实现 “Fragment 显示时才加载数据”。
示例 9:ViewPager2 + Fragment 实现懒加载
- 创建懒加载基类 Fragment(LazyLoadFragment):
public abstract class LazyLoadFragment extends Fragment {
private boolean isLoaded = false; // 是否已加载数据
private boolean isVisible = false; // 是否可见
private boolean isViewCreated = false; // 视图是否创建完成
// 视图创建完成
@Override
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
isViewCreated = true;
// 检查是否满足懒加载条件
checkLazyLoad();
}
// Fragment可见状态变化(ViewPager2切换时触发)
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
isVisible = isVisibleToUser;
checkLazyLoad();
}
// 检查懒加载条件:视图创建完成 + 可见 + 未加载数据
private void checkLazyLoad() {
if (isViewCreated && isVisible && !isLoaded) {
// 执行懒加载(子类实现)
lazyLoadData();
isLoaded = true; // 标记为已加载
}
}
// 懒加载数据(子类必须实现)
protected abstract void lazyLoadData();
// 重置加载状态(如Fragment被隐藏后重新显示,需再次加载)
public void resetLazyLoad() {
isLoaded = false;
checkLazyLoad();
}
}
- 子类 Fragment 继承基类,实现懒加载:
public class LazyPage1Fragment extends LazyLoadFragment {
private TextView tvLazy;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_lazy_page, container, false);
tvLazy = view.findViewById(R.id.tv_lazy);
return view;
}
// 实现懒加载方法:显示时才加载数据
@Override
protected void lazyLoadData() {
Log.d("LazyLoadTest", "页面1开始懒加载数据");
// 模拟网络请求(子线程)
new Thread(() -> {
try {
Thread.sleep(1000);
// 切换到主线程更新UI
requireActivity().runOnUiThread(() -> {
tvLazy.setText("页面1懒加载数据完成");
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
- 配置 ViewPager2 适配器,设置 MaxLifecycle:
public class LazyFragmentAdapter extends FragmentStateAdapter {
private List<LazyLoadFragment> fragmentList;
public LazyFragmentAdapter(@NonNull FragmentActivity fragmentActivity, List<LazyLoadFragment> fragments) {
super(fragmentActivity);
this.fragmentList = fragments;
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragmentList.get(position);
}
@Override
public int getItemCount() {
return fragmentList.size();
}
// 关键:设置Fragment的最大生命周期,控制懒加载
@Override
public void onAttachedToRecyclerView(@NonNull RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
ViewPager2 viewPager2 = (ViewPager2) recyclerView.getParent();
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
// 重置其他Fragment的加载状态
for (int i = 0; i < fragmentList.size(); i++) {
if (i != position) {
fragmentList.get(i).resetLazyLoad();
}
}
}
});
}
}
关键说明:
- 懒加载的核心是
setUserVisibleHint()(Fragment 可见状态变化回调); - 基类通过
isViewCreated“isVisible”“isLoaded” 三个状态控制懒加载时机; - 适用于网络请求、大数据加载等场景,优化 APP 启动速度和内存占用。
5.3 特殊 Fragment:DialogFragment、BottomSheetFragment
Android 提供了几种特殊的 Fragment,用于实现弹窗、底部弹窗等场景,比传统 Dialog 更灵活、易复用。
1. DialogFragment(弹窗 Fragment,替代 Dialog)
DialogFragment 是封装了 Dialog 的 Fragment,支持屏幕旋转后自动恢复,适合实现复杂弹窗(如登录弹窗、筛选弹窗)。
示例 10:DialogFragment 实现登录弹窗
public class LoginDialogFragment extends DialogFragment {
private EditText etUsername, etPassword;
private OnLoginListener mListener;
// 回调接口:登录成功后通知Activity
public interface OnLoginListener {
void onLoginSuccess(String username);
}
public void setOnLoginListener(OnLoginListener listener) {
mListener = listener;
}
// 设置弹窗样式(可选)
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置为对话框样式(无标题)
setStyle(STYLE_NO_TITLE, android.R.style.Theme_DeviceDefault_Dialog);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// 加载弹窗布局
View view = inflater.inflate(R.layout.fragment_login_dialog, container, false);
etUsername = view.findViewById(R.id.et_username);
etPassword = view.findViewById(R.id.et_password);
// 登录按钮点击事件
view.findViewById(R.id.btn_login).setOnClickListener(v -> {
String username = etUsername.getText().toString().trim();
String password = etPassword.getText().toString().trim();
if (!TextUtils.isEmpty(username) && !TextUtils.isEmpty(password)) {
// 模拟登录成功
if (mListener != null) {
mListener.onLoginSuccess(username);
}
dismiss(); // 关闭弹窗
} else {
Toast.makeText(getContext(), "请输入用户名和密码", Toast.LENGTH_SHORT).show();
}
});
// 取消按钮点击事件
view.findViewById(R.id.btn_cancel).setOnClickListener(v -> dismiss());
return view;
}
// 设置弹窗大小(可选)
@Override
public void onStart() {
super.onStart();
Dialog dialog = getDialog();
if (dialog != null) {
// 设置弹窗宽度为屏幕宽度的80%
Window window = dialog.getWindow();
WindowManager.LayoutParams params = window.getAttributes();
params.width = (int) (getResources().getDisplayMetrics().widthPixels * 0.8);
window.setAttributes(params);
}
}
}
// Activity中显示弹窗
public class DialogHostActivity extends AppCompatActivity implements LoginDialogFragment.OnLoginListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dialog_host);
// 点击按钮显示登录弹窗
findViewById(R.id.btn_show_login).setOnClickListener(v -> {
LoginDialogFragment dialogFragment = new LoginDialogFragment();
dialogFragment.setOnLoginListener(this);
// 显示弹窗(必须用show()方法)
dialogFragment.show(getSupportFragmentManager(), "login_dialog");
});
}
// 登录成功回调
@Override
public void onLoginSuccess(String username) {
Toast.makeText(this, "登录成功,欢迎" + username, Toast.LENGTH_SHORT).show();
}
}
2. BottomSheetFragment(底部弹窗 Fragment)
BottomSheetFragment 是 Material Design 提供的底部弹窗 Fragment,支持滑动展开 / 收起,适合实现底部菜单、筛选列表等场景(需添加 Material 依赖):
gradle
implementation 'com.google.android.material:material:1.9.0'
public class BottomSheetFragment extends BottomSheetDialogFragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_bottom_sheet, container, false);
// 底部弹窗布局(如菜单列表)
ListView lvMenu = view.findViewById(R.id.lv_menu);
String[] menuItems = {"选项1", "选项2", "选项3", "选项4"};
ArrayAdapter<String> adapter = new ArrayAdapter<>(
getContext(),
android.R.layout.simple_list_item_1,
menuItems
);
lvMenu.setAdapter(adapter);
// 菜单点击事件
lvMenu.setOnItemClickListener((parent, view1, position, id) -> {
Toast.makeText(getContext(), "选择了" + menuItems[position], Toast.LENGTH_SHORT).show();
dismiss();
});
return view;
}
}
// 显示底部弹窗
findViewById(R.id.btn_show_bottom_sheet).setOnClickListener(v -> {
BottomSheetFragment bottomSheetFragment = new BottomSheetFragment();
bottomSheetFragment.show(getSupportFragmentManager(), "bottom_sheet");
});
六、Fragment 常见坑点与避坑指南(实战经验总结)

6.1 坑点 1:Fragment 重叠问题(屏幕旋转后)
- 现象:屏幕旋转后,Fragment 重复加载,出现多个 Fragment 重叠显示;
- 原因:Activity 重建时,FragmentManager 会自动恢复已添加的 Fragment,若在 onCreate () 中再次 addFragment,会导致重复添加;
- 解决方案:
- 在 addFragment 前判断是否已添加(通过 tag 查找);
if (savedInstanceState == null) { // 仅在首次创建时添加Fragment,重建时不添加 getSupportFragmentManager().beginTransaction() .add(R.id.fragment_container, new TestFragment(), "test_tag") .commit(); }- 或通过
findFragmentByTag()查找已存在的 Fragment,避免重复创建。
6.2 坑点 2:Fragment 获取 Activity 为 null
- 现象:在 Fragment 的 onCreate () 中调用
getActivity()返回 null; - 原因:Fragment 的 onCreate () 执行时,可能还未与 Activity 完全关联;
- 解决方案:
- 延迟获取 Activity,在 onAttach () 或 onViewCreated () 中获取;
- 优先使用
requireActivity()(若 Activity 为 null 会抛出异常,便于排查)而非getActivity(); - 保存 Activity 引用时,使用 WeakReference 避免内存泄漏:
private WeakReference<Activity> mActivityRef; @Override public void onAttach(@NonNull Context context) { super.onAttach(context); mActivityRef = new WeakReference<>((Activity) context); } // 使用时 Activity activity = mActivityRef.get(); if (activity != null && !activity.isFinishing()) { // 执行操作 }
6.3 坑点 3:FragmentTransaction.commit () 报错(状态非法)
- 现象:调用 commit () 时抛出
IllegalStateException: Can not perform this action after onSaveInstanceState; - 原因:Activity 的 onSaveInstanceState () 执行后(如按 Home 键),系统已保存状态,此时不能再提交 Fragment 事务;
- 解决方案:
- 用
commitAllowingStateLoss()替代 commit ()(允许状态丢失,适合非关键事务); - 确保事务提交在 Activity 的 onSaveInstanceState () 之前(如 onCreate ()、onResume ());
- 避免在异步回调(如网络请求回调)中提交事务,需先判断 Activity 状态。
- 用
6.4 坑点 4:ViewPager2 预加载导致懒加载失效
- 现象:设置了懒加载,但 ViewPager2 仍会预加载相邻 Fragment;
- 原因:ViewPager2 默认预加载 1 个 Fragment,且 Fragment 的生命周期会提前执行;
- 解决方案:
- 结合
setMaxLifecycle()控制 Fragment 的生命周期状态; - 使用前面讲的懒加载基类,通过
setUserVisibleHint()判断可见状态。
- 结合
6.5 坑点 5:Fragment 内存泄漏
- 现象:应用退出后,LeakCanary 检测到内存泄漏;
- 常见原因:
- Fragment 持有 Activity 的强引用(如非静态内部类、Handler);
- 线程未停止(如在 Fragment 中启动的线程未在 onDestroy () 中中断);
- 监听器未注销(如广播接收器、EventBus 未注销);
- 解决方案:
- 避免持有 Activity 强引用,使用 WeakReference;
- 线程在 onDestroy () 中中断,监听器在对应的生命周期方法中注销;
- 非静态内部类改为静态内部类(如 Handler、Thread)。
七、总结:Fragment 核心知识点图谱

到这里,Fragment 的核心内容已经全部讲完,我们用一张图谱梳理重点:
Fragment核心知识点
├── 基础认知:模块化UI组件、与Activity的区别、典型场景
├── 生命周期:11个核心回调、与Activity联动、状态保存(onSaveInstanceState/ViewModel)
├── 加载方式:
│ - 静态加载:布局声明、简单场景
│ - 动态加载:FragmentManager/FragmentTransaction、add/replace/remove/hide/show、回退栈
├── 通信方式:
│ - Fragment→Activity:接口回调/ViewModel
│ - Activity→Fragment:Bundle传参/ViewModel
│ - Fragment→Fragment:ViewModel共享/Activity中转
├── 进阶用法:
│ - ViewPager2+Fragment:滑动切换、适配器
│ - 懒加载:按需加载数据、基类封装
│ - 特殊Fragment:DialogFragment(弹窗)、BottomSheetFragment(底部弹窗)
├── 避坑指南:重叠问题、Activity为null、事务提交错误、预加载失效、内存泄漏
其实 Fragment 的学习关键是 “理解模块化思想 + 掌握生命周期联动”—— 它的核心价值是拆分复杂页面、实现 UI 复用,只要理清生命周期的联动逻辑,掌握动态操作和通信方式,就能避开大部分坑。
把文中的示例逐个敲一遍,结合日志观察执行流程,再在实际项目中应用,很快就能熟练掌握。如果遇到具体问题,可以在评论区留言,我会第一时间回复~
Android Fragment全面解析
1548

被折叠的 条评论
为什么被折叠?



