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(推荐)

使用 FragmentManagerFragmentTransaction 动态添加、替换、移除 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 不应直接相互调用,应通过 宿主 ActivityViewModel 进行通信。

方案 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);

七、最佳实践与避坑指南

  1. 永远不要在 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;
    }
  2. 使用 requireContext() / requireActivity()
    替代 getContext() / getActivity(),避免空指针。

  3. onDestroyView() 中清理 View 引用
    防止内存泄漏。

  4. 使用 ViewLifecycleOwner
    观察 LiveData 时使用 getViewLifecycleOwner() 而非 getLifecycleOwner()

  5. 避免在 onCreate() 中执行耗时操作

八、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

posted @ 2025-10-12 12:41  wzzkaifa  阅读(6)  评论(0)    收藏  举报