Android Fragment 详解
Fragment(片段)是 Android 3.0 (API level 11) 引入的重要组件,用于构建灵活、模块化的用户界面,特别适合平板电脑等大屏幕设备。
Fragment 基本概念
什么是 Fragment
- UI 模块:代表 Activity 中的一部分行为或用户界面
- 可重用组件:可以在多个 Activity 中重复使用
- 独立生命周期:拥有自己的生命周期,但受宿主 Activity 影响
- 响应配置变化:比 Activity 更适合处理屏幕旋转等配置变化
Fragment 与 Activity 的关系
- Fragment 必须嵌入到 Activity 中
- 一个 Activity 可以包含多个 Fragment
- Fragment 的生命周期受宿主 Activity 影响
- Fragment 可以通过 Activity 实现与其他 Fragment 的通信
Fragment 生命周期
Fragment 生命周期比 Activity 更复杂,包含以下回调方法:
- onAttach() - Fragment 与 Activity 关联时调用
- onCreate() - Fragment 创建时调用
- onCreateView() - 创建 Fragment 的视图层次结构
- onActivityCreated() - Activity 的 onCreate() 完成后调用
- onStart() - Fragment 可见时调用
- onResume() - Fragment 可交互时调用
- onPause() - Fragment 不再可交互时调用
- onStop() - Fragment 不可见时调用
- onDestroyView() - 移除与 Fragment 关联的视图层次结构
- onDestroy() - Fragment 状态最终清理时调用
- onDetach() - Fragment 与 Activity 解除关联时调用
创建 Fragment
1. 创建 Fragment 类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| public class MyFragment extends Fragment {
public MyFragment() {
// 必须有一个空构造函数
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// 膨胀 Fragment 的布局
return inflater.inflate(R.layout.fragment_my, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// 初始化视图组件
Button button = view.findViewById(R.id.button);
button.setOnClickListener(v -> {
// 处理点击事件
});
}
}
|
2. 创建 Fragment 布局文件
res/layout/fragment_my.xml
:
1
2
3
4
5
6
7
8
9
10
11
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me" />
</LinearLayout>
|
添加 Fragment 到 Activity
方式1: 静态添加(XML布局)
1
2
3
4
5
6
7
8
9
10
11
| <!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/myFragment"
android:name="com.example.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
|
方式2: 动态添加(代码)
1
2
3
4
5
6
7
8
| // 获取 FragmentManager
FragmentManager fragmentManager = getSupportFragmentManager();
// 开始事务
FragmentTransaction transaction = fragmentManager.beginTransaction();
// 添加 Fragment
transaction.add(R.id.fragment_container, new MyFragment());
// 提交事务
transaction.commit();
|
Fragment 事务
FragmentTransaction 允许对 Fragment 执行添加、移除、替换等操作:
1
2
3
4
5
6
7
8
9
| FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// 替换 Fragment
transaction.replace(R.id.fragment_container, new AnotherFragment());
// 添加到返回栈,按返回键可回到前一个 Fragment
transaction.addToBackStack(null);
transaction.commit();
|
Fragment 间通信
1. 通过 Activity 中介
1
2
3
4
5
6
7
8
9
| // 在 Fragment 中
((HostActivity) getActivity()).communicateWithOtherFragment(data);
// 在 Activity 中
public void communicateWithOtherFragment(String data) {
OtherFragment fragment = (OtherFragment) getSupportFragmentManager()
.findFragmentById(R.id.other_fragment);
fragment.receiveData(data);
}
|
2. 使用接口回调
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
| // 在 Fragment 中定义接口
public interface OnFragmentInteractionListener {
void onFragmentInteraction(String data);
}
// Activity 实现接口
public class MainActivity extends AppCompatActivity implements MyFragment.OnFragmentInteractionListener {
@Override
public void onFragmentInteraction(String data) {
// 处理来自 Fragment 的数据
}
}
// 在 Fragment 的 onAttach 中设置监听器
@Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
listener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
// 发送数据
listener.onFragmentInteraction("Hello from Fragment!");
|
3. 使用 ViewModel (推荐)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| // 共享的 ViewModel
public class SharedViewModel extends ViewModel {
private final MutableLiveData<String> selected = new MutableLiveData<>();
public void select(String item) {
selected.setValue(item);
}
public LiveData<String> getSelected() {
return selected;
}
}
// 在 Fragment 中
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// 更新 UI
});
// 发送数据
model.select("New data");
|
Fragment 类型
1. 普通 Fragment
基本的 UI Fragment,如上例所示
2. DialogFragment
显示对话框的专用 Fragment:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| public class MyDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("提示")
.setMessage("这是一个对话框")
.setPositiveButton("确定", (dialog, id) -> {});
return builder.create();
}
}
// 显示对话框
MyDialogFragment dialog = new MyDialogFragment();
dialog.show(getSupportFragmentManager(), "MyDialog");
|
3. PreferenceFragmentCompat
用于创建设置界面:
1
2
3
4
5
6
| public class SettingsFragment extends PreferenceFragmentCompat {
@Override
public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
setPreferencesFromResource(R.xml.preferences, rootKey);
}
}
|
Fragment 常与 ViewPager 结合实现滑动标签页:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
| public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}
@Override
public Fragment getItem(int position) {
// 返回对应位置的 Fragment
return PlaceholderFragment.newInstance(position + 1);
}
@Override
public int getCount() {
return 3; // 页数
}
}
// 在 Activity 中设置
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(new SectionsPagerAdapter(getSupportFragmentManager()));
|
Fragment 最佳实践
- 避免在 Fragment 中直接引用其他 Fragment:使用接口或 ViewModel 通信
- 正确处理配置变化:使用 setRetainInstance(true) 保留非 UI 数据(已废弃,推荐使用 ViewModel)
- 谨慎使用 addToBackStack:避免创建过深的返回栈
- 使用 FragmentFactory:AndroidX 提供 FragmentFactory 来改进 Fragment 实例化
- 注意内存泄漏:避免在 Fragment 中持有 Activity 的长生命周期引用
- 使用 Navigation 组件:简化 Fragment 导航和管理
常见问题解决方案
1. Fragment 重叠问题
在 Activity 的 onCreate 中添加 Fragment 时检查 savedInstanceState:
1
2
3
4
5
| if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new MyFragment())
.commit();
}
|
2. getActivity() 返回 null
确保在 Fragment 生命周期正确阶段调用,或使用 requireActivity()
3. 视图查找时机
在 onViewCreated() 中执行视图查找和初始化,而不是 onCreateView()
4. 正确处理用户可见性
使用 setUserVisibleHint() (已废弃) 或 ViewPager2 的 FragmentStateAdapter 的 BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT
现代 Fragment 开发 (AndroidX)
- 使用 FragmentContainerView 替代 FrameLayout 作为 Fragment 容器
- 使用新的 Fragment 结果 API 替代 startActivityForResult
1
2
3
4
5
6
7
8
9
| // 设置结果监听器
setFragmentResultListener("requestKey", this, (key, bundle) -> {
// 处理结果
});
// 发送结果
Bundle result = new Bundle();
result.putString("data", "some data");
setFragmentResult("requestKey", result);
|
- 使用 Navigation 组件 管理 Fragment 导航
- 使用 ViewModel 和 LiveData 管理数据
Fragment 是 Android 开发中构建灵活 UI 的强大工具,合理使用可以创建适应不同屏幕尺寸的高质量应用。随着 Android 开发的发展,Fragment 的最佳实践也在不断演进,建议结合最新的 Jetpack 组件使用。