Android编写-Fragment
一、什么是 Fragment?
1. 核心概念
- Fragment 是一个可以嵌入在
Activity
中的 UI 片段。 - 它拥有自己的布局、生命周期和事件处理逻辑。
- 一个 Activity 可以包含多个 Fragment,一个 Fragment 也可以在多个 Activity 中复用。
2. 为什么使用 Fragment?
场景 | 优势 |
---|---|
大屏适配 | 在平板上并排显示列表 + 详情页 |
Tab 切换 | ViewPager + Fragment 实现标签页 |
组件化 | 将功能模块(如设置、消息)独立封装 |
状态保留 | 横竖屏切换时自动保存 UI 状态 |
二、Fragment 的生命周期
Fragment 的生命周期比 Activity 更复杂,因为它依赖于宿主 Activity 并可能被动态添加/移除。
1. 生命周期图谱
onAttach() → onCreate() → onCreateView() → onActivityCreated()
→ onStart() → onResume()
↗ (用户交互) ↘
onPause() ← onStop() ← onDestroyView() ← onDestroy() ← onDetach()
2. 关键回调详解
方法 | 说明 | 注意事项 |
---|---|---|
onAttach() | Fragment 与 Activity 关联 | 可获取 Activity 引用 |
onCreate() | 初始化 Fragment | 避免耗时操作 |
onCreateView() | 创建并返回 UI 布局 | inflate 布局文件 |
onViewCreated() | View 创建完成 | 初始化 View(如 findViewById) |
onActivityCreated() | Activity 的 onCreate() 完成 | 可安全调用 Activity 方法 |
onStart() / onResume() | 可见、可交互 | 类似 Activity |
onPause() / onStop() | 不可见 | 释放资源 |
onDestroyView() | View 被销毁 | 清理 View 相关资源(如 ButterKnife.unbind) |
onDestroy() | Fragment 销毁 | 最终清理 |
onDetach() | 与 Activity 解除关联 | 置空 Activity 引用 |
⚠️ 重点:
onDestroyView()
和onDestroy()
可能不同时触发。例如,replace()
操作会触发onDestroyView()
但不立即触发onDestroy()
。
三、创建与使用 Fragment
1. 定义 Fragment
public class NewsListFragment extends Fragment {
private RecyclerView recyclerView;
private NewsAdapter adapter;
// 必须提供空参构造函数
public NewsListFragment() {}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// 加载布局
return inflater.inflate(R.layout.fragment_news_list, container, false);
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// 初始化 View
recyclerView = view.findViewById(R.id.recycler_view);
adapter = new NewsAdapter();
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(requireContext()));
loadData();
}
private void loadData() {
// 模拟加载数据
List newsList = NewsRepository.getNews();
adapter.submitList(newsList);
}
}
2. 在 Activity 中静态添加(不推荐)
❌ 缺点:无法动态管理,不灵活。
四、动态管理 Fragment(推荐)
使用 FragmentManager
和 FragmentTransaction
动态添加、替换、移除 Fragment。
1. 在 Activity 布局中预留容器
2. 使用 FragmentTransaction
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
// 首次创建,添加 Fragment
getSupportFragmentManager()
.beginTransaction()
.add(R.id.fragment_container, new NewsListFragment())
.commit();
}
}
}
3. 常用操作
FragmentManager fm = getSupportFragmentManager();
// 添加
fm.beginTransaction()
.add(R.id.container, fragment)
.commit();
// 替换(常用)
fm.beginTransaction()
.replace(R.id.container, newFragment)
.addToBackStack("tag") // 加入返回栈
.commit();
// 移除
fm.beginTransaction()
.remove(fragment)
.commit();
// 隐藏/显示(切换)
fm.beginTransaction()
.hide(fragment1)
.show(fragment2)
.commit();
✅ 最佳实践:
- 使用
replace()
实现页面切换。- 调用
addToBackStack()
实现返回键导航。- 调用
commit()
提交事务。
五、Fragment 之间的通信
Fragment 不应直接相互调用,应通过 宿主 Activity 或 ViewModel 进行通信。
方案 1:通过 Activity 通信(传统方式)
(1) 定义接口
public interface OnNewsSelectedListener {
void onNewsSelected(News news);
}
(2) Fragment 中触发事件
// NewsListFragment.java
private OnNewsSelectedListener listener;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof OnNewsSelectedListener) {
listener = (OnNewsSelectedListener) context;
} else {
throw new RuntimeException(context + " must implement OnNewsSelectedListener");
}
}
// 在点击时调用
listener.onNewsSelected(selectedNews);
(3) Activity 实现接口并转发
public class MainActivity extends AppCompatActivity implements OnNewsSelectedListener {
@Override
public void onNewsSelected(News news) {
NewsDetailFragment detailFragment = NewsDetailFragment.newInstance(news);
getSupportFragmentManager().beginTransaction()
.replace(R.id.fragment_container, detailFragment)
.addToBackStack(null)
.commit();
}
}
方案 2:使用 ViewModel(推荐 - Jetpack)
(1) 创建共享 ViewModel
public class SharedViewModel extends ViewModel {
private final MutableLiveData selectedNews = new MutableLiveData<>();
public LiveData getSelectedNews() {
return selectedNews;
}
public void selectNews(News news) {
selectedNews.setValue(news);
}
}
(2) Fragment 中观察数据
// NewsDetailFragment.java
public class NewsDetailFragment extends Fragment {
private SharedViewModel viewModel;
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
viewModel = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
viewModel.getSelectedNews().observe(getViewLifecycleOwner(), news -> {
if (news != null) {
displayNews(news);
}
});
}
}
(3) 另一个 Fragment 发送数据
// NewsListFragment.java
viewModel.selectNews(selectedNews); // 发送选中新闻
✅ 优势:解耦、生命周期安全、支持配置变更。
六、与 Jetpack Navigation 集成
现代 Android 开发推荐使用 Navigation 组件统一管理 Fragment 导航。
1. 添加依赖
implementation "androidx.navigation:navigation-fragment:2.7.7"
implementation "androidx.navigation:navigation-ui:2.7.7"
2. 定义导航图(nav_graph.xml)
3. 使用 Navigation Controller
// 在 Fragment 中跳转
findNavController().navigate(R.id.action_to_detail);
// 带参数跳转
Bundle bundle = new Bundle();
bundle.putParcelable("news", selectedNews);
findNavController().navigate(R.id.action_to_detail, bundle);
七、最佳实践与避坑指南
永远不要在 Fragment 构造函数中传递参数
✅ 使用newInstance()
静态工厂方法:public static NewsDetailFragment newInstance(News news) { Bundle args = new Bundle(); args.putParcelable("news", news); NewsDetailFragment fragment = new NewsDetailFragment(); fragment.setArguments(args); return fragment; }
使用
requireContext()
/requireActivity()
替代getContext()
/getActivity()
,避免空指针。在
onDestroyView()
中清理 View 引用
防止内存泄漏。使用
ViewLifecycleOwner
观察 LiveData 时使用getViewLifecycleOwner()
而非getLifecycleOwner()
。避免在
onCreate()
中执行耗时操作
八、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!