Kotlin中实现RecyclerView点击跳转详情Fragment并传递数据
文章列表跳转详情页实现方案
效果示例

核心问题梳理
原有代码存在3个逻辑错误:
- Adapter内点击事件仅创建了Article对象,未向外传递点击事件、未触发跳转逻辑
- Fragment间传参错误使用了Activity跳转的Intent API,Fragment传参需要通过
argumentsBundle携带数据 - 列表所在的HomeFragment中未实现Fragment跳转调度逻辑
分步实现
1. 配置Article实体类支持序列化
确保Article实体类实现Parcelable接口,支持组件间数据传递,Kotlin可直接用@Parcelize注解简化模板代码:
import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize data class Article( val title: String, val img: String, val content: String ) : Parcelable
注意:使用@Parcelize需要在模块级build.gradle的plugins块中添加id 'kotlin-parcelize'插件
2. 修改ArticleAdapter,增加点击事件回调
Adapter不直接持有Fragment导航能力,通过高阶函数将点击事件抛给外层宿主处理,避免代码耦合:
class ArticleAdapter(private val articleList: ArrayList<Article>) : RecyclerView.Adapter<ArticleAdapter.MyViewHolder>() { // 声明点击事件回调,入参为被点击的文章对象 private var onItemClickListener: ((Article) -> Unit)? = null // 对外提供监听设置入口 fun setOnItemClickListener(listener: (Article) -> Unit) { onItemClickListener = listener } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder { val binding = ItemArticleBinding.inflate(LayoutInflater.from(parent.context), parent, false) return MyViewHolder(binding) } override fun onBindViewHolder(holder: MyViewHolder, position: Int) { val data = articleList[position] holder.bind(data, onItemClickListener) } class MyViewHolder(private val binding: ItemArticleBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(data: Article, clickListener: ((Article) -> Unit)?) { Glide.with(binding.root.context) .load(data.img) .into(binding.articleImage) binding.articleTitle.text = data.title binding.root.setOnClickListener { // 点击时触发回调,传入当前条目对应的文章数据 clickListener?.invoke(data) } } } override fun getItemCount(): Int = articleList.size }
3. 改造DetailArticleFragment,通过arguments接收参数
删除原有错误的Intent取值逻辑,通过Fragment自带的arguments获取参数,新增newInstance工厂方法统一传参入口,避免外部写错参数Key:
class DetailArticleFragment : Fragment() { private var _binding: FragmentDetailArticleBinding? = null private val binding get() = _binding!! override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentDetailArticleBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 从arguments中获取序列化的文章对象,做合法性校验 val article = arguments?.getParcelable<Article>(DETAIL_ARTICLE) ?: throw IllegalArgumentException("缺少文章参数,无法打开详情页") // 渲染页面数据 Glide.with(this) .load(article.img) .into(binding.articleImage) binding.articleTitle.text = article.title binding.articleDescription.text = article.content } override fun onDestroyView() { super.onDestroyView() _binding = null } companion object { private const val DETAIL_ARTICLE = "detail_article" // 统一传参入口 fun newInstance(article: Article): DetailArticleFragment { val fragment = DetailArticleFragment() val bundle = Bundle().apply { putParcelable(DETAIL_ARTICLE, article) } fragment.arguments = bundle return fragment } } }
4. 在HomeFragment中实现跳转调度
在HomeFragment初始化RecyclerView的位置,给Adapter设置点击监听,点击条目时执行Fragment跳转:
// HomeFragment中初始化Adapter的代码段 val articleAdapter = ArticleAdapter(articleList) // 设置条目点击监听 articleAdapter.setOnItemClickListener { clickedArticle -> // 两种跳转方式二选一即可 // 方式1:使用Navigation组件导航(推荐) val bundle = bundleOf(DetailArticleFragment.DETAIL_ARTICLE to clickedArticle) findNavController().navigate(R.id.action_homeFragment_to_detailArticleFragment, bundle) // 方式2:使用原生FragmentTransaction跳转 // parentFragmentManager.beginTransaction() // .replace(R.id.fragment_container, DetailArticleFragment.newInstance(clickedArticle)) // .addToBackStack(null) // .commit() } // 给RecyclerView绑定adapter binding.articleRv.adapter = articleAdapter
注意事项
- 如果使用Navigation组件跳转,不需要手动调用newInstance方法,直接通过bundle传参即可,Navigation会自动将bundle设置为目标Fragment的arguments
- 不要在Adapter中直接执行Fragment跳转逻辑,会造成代码耦合、提升内存泄漏风险
- 接收参数时必须做好判空校验,避免因参数缺失导致崩溃
内容的提问来源于stack exchange,提问作者micinable
相关产品推荐
相关产品推荐

