如何在单个ViewPager中展示Room数据库两张表的数据?
问题解决:同一ViewPager适配不同Room数据源
核心问题
当前ViewPagerAdapter仅支持List<QuotesByCategory>类型的数据源,但从收藏表获取的是List<Favourite>,类型不匹配导致报错。无需为两个场景创建单独的ViewPager,只需统一数据模型或调整适配器兼容性即可。
解决方案:统一通用数据模型(推荐)
1. 创建通用QuoteItem数据类
定义一个包含两个数据源核心字段的通用类,抹平实体差异:
data class QuoteItem( val id: Int, val quoteContent: String, val categoryName: String? = null // 收藏数据可能无分类,设为可空 )
2. 修改QuoteDetailFragment的数据映射逻辑
在收集Room数据时,将不同实体转换为QuoteItem列表:
val incomingFragment = arguments?.getString(Constants.STRING_EXTRA_INCOMING_FRAGMENT) val db = AppDatabase.getDatabase(requireContext()).quoteDao() viewPager = view.findViewById(R.id.viewPager) if (incomingFragment != null && incomingFragment.contains(Constants.FRAGMENT_QUOTE)){ lifecycle.coroutineScope.launch { db.getQuotesByCategory(categoryId).collect { quotesByCategoryList -> // 将QuotesByCategory转换为QuoteItem val quoteItems = quotesByCategoryList.flatMap { categoryGroup -> categoryGroup.quotes.map { quote -> QuoteItem( id = quote.id, quoteContent = quote.quote, categoryName = categoryGroup.category.categoryName ) } } adapter = ViewPagerAdapter(requireContext(), quoteItems, lifecycleScope) viewPager.adapter = adapter viewPager.setCurrentItem(position, false) } } } if (incomingFragment != null && incomingFragment.contains(Constants.FRAGMENT_FAVOURITE)){ lifecycle.coroutineScope.launch { db.getAllFavourites().collect { favouriteList -> // 将Favourite转换为QuoteItem val quoteItems = favouriteList.map { favourite -> QuoteItem( id = favourite.id, quoteContent = favourite.quote, categoryName = favourite.categoryName // 若Favourite无此字段,设为null ) } adapter = ViewPagerAdapter(requireContext(), quoteItems, lifecycleScope) viewPager.adapter = adapter viewPager.setCurrentItem(position, false) } } }
3. 调整ViewPagerAdapter适配通用模型
修改适配器接收List<QuoteItem>,并优化协程避免内存泄漏:
class ViewPagerAdapter( private val context: Context, private val quoteList: List<QuoteItem>, private val lifecycleScope: LifecycleCoroutineScope ) : RecyclerView.Adapter<ViewPagerAdapter.ViewHolder>() { inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val tvQuotes: TextView = view.findViewById(R.id.vp_quotes) val favouriteBtn: ImageButton = view.findViewById(R.id.vp_fav) // 若需显示分类,可添加对应TextView // val tvCategory: TextView = view.findViewById(R.id.vp_category) } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context) .inflate(R.layout.quote_detail_viewpager, parent, false) return ViewHolder(view) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val currentQuote = quoteList[position] holder.tvQuotes.text = currentQuote.quoteContent // 处理分类显示(可选) // currentQuote.categoryName?.let { // holder.tvCategory.text = it // holder.tvCategory.visibility = View.VISIBLE // } ?: run { // holder.tvCategory.visibility = View.GONE // } val favouritesDao = AppDatabase.getDatabase(context).quoteDao() lifecycleScope.launch(Dispatchers.IO) { val isFavourite = favouritesDao.isFavourite(currentQuote.id) withContext(Dispatchers.Main) { holder.favouriteBtn.setImageResource( if (isFavourite) R.drawable.ic_favorite else R.drawable.ic_favorite_border ) } } } override fun getItemCount(): Int = quoteList.size }
注意:原代码中使用独立
CoroutineScope易引发内存泄漏,改为传入Fragment的lifecycleScope确保协程随页面生命周期销毁。
备选方案:适配器重载构造方法
若不想创建通用数据类,可给适配器添加重载构造方法,分别接收两种数据源并内部处理,但这种方式会让适配器逻辑更复杂,不如通用模型清晰。
内容的提问来源于stack exchange,提问作者John doe
相关产品推荐
相关产品推荐

