使用Navigation Components实现RecyclerView点击切换Fragment报错
报错原因
- 核心问题是你在
RecyclerViewAdapterForClassList内部直接写导航逻辑、硬引用SecondFragment实例找NavController:- Adapter不属于导航组件的生命周期关联对象,直接持有外部Fragment强引用,很容易因为Fragment视图销毁、实例状态异常(比如已经被移除出返回栈)导致NavController查找失败,还会引发内存泄漏。
- 你没有从当前视图树绑定的导航宿主获取NavController,直接传入硬编码的Fragment实例,会出现NavController和当前导航宿主不匹配的崩溃。
- 如果排除上面的问题,还要检查nav_graph配置里
action_SecondFragment_to_studentListFragment这个action是否真的挂载在SecondFragment对应的destination节点下,是否存在id写错的情况。
正确实现方案
1. 改造Adapter,剥离导航逻辑
Adapter只负责视图渲染和点击事件分发,不要在内部持有Fragment/Activity强引用、写页面跳转逻辑,示例代码(Kotlin写法逻辑完全一致):
public class RecyclerViewAdapterForClassList extends RecyclerView.Adapter<RecyclerViewAdapterForClassList.VH> { private List<ClassBean> classList; // 定义点击回调接口 public interface OnItemClickListener { void onItemClick(ClassBean itemData); } private OnItemClickListener clickListener; public RecyclerViewAdapterForClassList(List<ClassBean> list, OnItemClickListener listener) { this.classList = list; this.clickListener = listener; } @NonNull @Override public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_class_list, parent, false); return new VH(view); } @Override public void onBindViewHolder(@NonNull VH holder, int position) { ClassBean item = classList.get(position); // 正常绑定视图数据即可 holder.tvClassName.setText(item.getClassName()); } @Override public int getItemCount() { return classList == null ? 0 : classList.size(); } class VH extends RecyclerView.ViewHolder { TextView tvClassName; public VH(@NonNull View itemView) { super(itemView); tvClassName = itemView.findViewById(R.id.tv_class_name); // 点击时触发回调,把数据抛给外部处理 itemView.setOnClickListener(v -> { int pos = getAdapterPosition(); if (pos != RecyclerView.NO_POSITION && clickListener != null) { clickListener.onItemClick(classList.get(pos)); } }); } } }
2. 在SecondFragment内处理导航逻辑
在Fragment自己的生命周期范围内初始化Adapter,在点击回调里获取NavController执行跳转,保证生命周期匹配:
public class SecondFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_second, container, false); } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); RecyclerView rvClassList = view.findViewById(R.id.rv_class_list); rvClassList.setLayoutManager(new LinearLayoutManager(requireContext())); // 初始化Adapter,传入点击监听 List<ClassBean> dataList = new ArrayList<>(); // 替换成你的真实数据源 RecyclerViewAdapterForClassList adapter = new RecyclerViewAdapterForClassList(dataList, item -> { // 在Fragment自身上下文中找NavController,不会出现生命周期不匹配问题 NavController navController = NavHostFragment.findNavController(SecondFragment.this); // 需要传参的话可以通过Bundle构造参数传入 navController.navigate(R.id.action_SecondFragment_to_studentListFragment); }); rvClassList.setAdapter(adapter); } }
注意事项
- 不要在Adapter中直接持有Fragment、Activity的强引用,所有业务逻辑、页面跳转都通过接口回调抛给对应页面处理,避免内存泄漏和生命周期不同步引发的崩溃。
- 导航调用必须在Fragment视图创建完成后执行,禁止在
onCreateView视图未返回时提前触发导航。 - 如果导航依然报错,检查nav_graph配置:确认studentListFragment是SecondFragment的直接导航目的地,对应action的id和代码中引用的id完全一致。
内容的提问来源于stack exchange,提问作者Aamir Ansari
相关产品推荐
相关产品推荐

