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
相关产品推荐
相关产品推荐

