You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RecyclerView点击跳转新Fragment展示Firestore对应文档数据

解决方案

1. 给Card类添加文档ID字段并实现Parcelable

Firestore的toObject()不会自动映射文档ID到实体类,需要手动添加该字段;同时为了在Fragment间传递Card对象,让它实现Parcelable:

data class Card(
    val name: String? = null,
    val description: String? = null,
    val img: String? = null,
    // 添加文档ID字段
    var documentId: String? = null
) : Parcelable {
    constructor(parcel: Parcel) : this(
        parcel.readString(),
        parcel.readString(),
        parcel.readString(),
        parcel.readString()
    )

    override fun writeToParcel(parcel: Parcel, flags: Int) {
        parcel.writeString(name)
        parcel.writeString(description)
        parcel.writeString(img)
        parcel.writeString(documentId)
    }

    override fun describeContents(): Int {
        return 0
    }

    companion object CREATOR : Parcelable.Creator<Card> {
        override fun createFromParcel(parcel: Parcel): Card {
            return Card(parcel)
        }

        override fun newArray(size: Int): Array<Card?> {
            return arrayOfNulls(size)
        }
    }
}

2. 修改ViewModel,为Card绑定文档ID

在获取Firestore文档时,把每个文档的ID赋值给对应的Card对象:

class MainViewModel: ViewModel() {
    private val db = FirebaseFirestore.getInstance()
    private val auth = FirebaseAuth.getInstance()
    private val documentsLiveData = MutableLiveData<List<Card>>()

    fun getDocuments() {
        auth.currentUser?.let { user ->
            db.collection("cards")
                .get()
                .addOnSuccessListener { documents ->
                    val cardList = mutableListOf<Card>()
                    for (document in documents) {
                        val card = document.toObject(Card::class.java)
                        // 绑定文档ID到Card对象
                        card.documentId = document.id
                        cardList.add(card)
                    }
                    documentsLiveData.value = cardList
                }
                .addOnFailureListener { exception ->
                    Log.e("Document error", exception.message.toString())
                }
        } ?: run {
            Log.e("Document error", "用户未登录")
        }
    }

    fun getDocumentsLiveData(): LiveData<List<Card>> {
        return documentsLiveData
    }
}

3. 修改Adapter,添加项点击回调

给Adapter添加点击接口,让MainFragment接收点击事件并处理跳转:

class CardAdapter(
    private val context: Context,
    private val list: List<Card>,
    // 点击回调接口
    private val onCardClick: (Card) -> Unit
) : RecyclerView.Adapter<CardAdapter.MyViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val item = list[position]
        holder.itemView.card_name.text = item.name
        holder.itemView.card_description.text = item.description
        Glide.with(context)
            .load(item.img)
            .into(holder.itemView.card_img)
        
        // 设置项点击事件
        holder.itemView.setOnClickListener {
            onCardClick(item)
        }
    }

    override fun getItemCount(): Int {
        return list.size
    }

    class MyViewHolder(view: View) : RecyclerView.ViewHolder(view)
}

4. 在MainFragment中处理点击,跳转到详情页

初始化Adapter时传入点击回调,通过导航组件或FragmentManager跳转到DetailFragment并传递Card数据:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewModel = ViewModelProvider(this)[MainViewModel::class.java]

    viewModel.getDocumentsLiveData().observe(viewLifecycleOwner) { cardList ->
        binding.progBar.visibility = View.GONE
        // 传入点击回调
        val adapter = CardAdapter(requireContext(), cardList) { card ->
            // 方式1:用导航组件(推荐)
            findNavController().navigate(
                MainFragmentDirections.actionMainFragmentToDetailFragment(card)
            )
            
            // 方式2:用FragmentManager
            // val detailFragment = DetailFragment.newInstance(card)
            // parentFragmentManager.beginTransaction()
            //     .replace(R.id.container, detailFragment)
            //     .addToBackStack(null)
            //     .commit()
        }
        binding.mainRecycler.adapter = adapter
    }
    viewModel.getDocuments()
}

5. 实现DetailFragment展示完整字段

创建详情页布局和ViewModel,通过文档ID获取完整数据(包括子集合):

DetailViewModel

class DetailViewModel: ViewModel() {
    private val db = FirebaseFirestore.getInstance()
    private val cardLiveData = MutableLiveData<Card>()
    // 替换成你的子集合实体类
    private val subCollectionLiveData = MutableLiveData<List<Any>>()

    fun getCardById(documentId: String) {
        db.collection("cards").document(documentId)
            .get()
            .addOnSuccessListener { document ->
                val card = document.toObject(Card::class.java)
                card?.documentId = document.id
                cardLiveData.value = card
            }
            .addOnFailureListener { exception ->
                Log.e("Detail error", exception.message.toString())
            }
    }

    // 加载子集合数据(如果需要)
    fun getCardSubCollection(documentId: String) {
        db.collection("cards").document(documentId)
            .collection("你的子集合名称")
            .get()
            .addOnSuccessListener { documents ->
                val subList = documents.map { it.toObject(你的子集合实体类::class.java) }
                subCollectionLiveData.value = subList
            }
    }

    fun getCardLiveData(): LiveData<Card> = cardLiveData
    fun getSubCollectionLiveData(): LiveData<List<Any>> = subCollectionLiveData
}

DetailFragment

class DetailFragment : Fragment() {
    private lateinit var binding: FragmentDetailBinding
    private lateinit var viewModel: DetailViewModel

    // 非导航组件时,用newInstance传递参数
    companion object {
        private const val ARG_CARD = "card"
        fun newInstance(card: Card): DetailFragment {
            val fragment = DetailFragment()
            val args = Bundle()
            args.putParcelable(ARG_CARD, card)
            fragment.arguments = args
            return fragment
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentDetailBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel = ViewModelProvider(this)[DetailViewModel::class.java]

        // 获取传递的Card数据
        val card = arguments?.getParcelable<Card>(ARG_CARD) 
            ?: navArgs<DetailFragmentArgs>().card // 导航组件安全参数方式

        // 加载完整数据
        viewModel.getCardById(card.documentId!!)
        viewModel.getCardSubCollection(card.documentId!!)

        // 更新UI
        viewModel.getCardLiveData().observe(viewLifecycleOwner) { loadedCard ->
            binding.detailName.text = loadedCard.name
            binding.detailDescription.text = loadedCard.description
            Glide.with(requireContext())
                .load(loadedCard.img)
                .into(binding.detailImg)
            // 展示其他字段...
        }

        // 处理子集合数据
        viewModel.getSubCollectionLiveData().observe(viewLifecycleOwner) { subList ->
            // 比如用RecyclerView展示子集合内容
        }
    }
}

关键说明

  • 之前ID查询失败的核心原因是Card类未保存Firestore文档ID,导致无法定位到具体文档;
  • 传递Card对象时,Parcelable是最直接的方式,也可以只传递documentId,在详情页重新查询;
  • 若使用导航组件,推荐用安全参数传递数据,避免类型错误。

内容的提问来源于stack exchange,提问作者slatter1

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 07:09:58