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

如何在单个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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 02:37:56