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

Android RecyclerView Adapter中Cursor查询结果无法遍历仅显示单条记录怎么办

问题根因

onBindViewHolder 会为每个列表项单独执行,你当前的写法每次执行都会重新查询全量符合 type=2 条件的记录,但是每次仅调用 cursor.moveToFirst() 定位到第一条记录,因此无论当前绑定的是第几个列表位置,永远只会读取第一条数据,和你加不加 moveToNext 没有关系。

最优解决方案(推荐)

不要在 onBindViewHolder 中执行数据库查询,该方法会频繁调用,频繁查询数据库会严重降低列表滑动性能。你应该提前将符合条件的数据一次性查出来转换成实体类列表,交由Adapter持有使用:

  1. 在Adapter中声明数据列表成员变量,提供数据刷新方法
// 自定义实体类,按需存储需要用到的字段
data class FoodItem(
    val type: String,
    val name: String,
    val desc: String,
    val imgUri: String
)

class YourAdapter : RecyclerView.Adapter<MyViewHolder> {
    // 持有数据的列表
    private var itemList = emptyList<FoodItem>()

    // 对外暴露刷新数据的方法,数据变化时调用即可
    fun loadDataFromDB() {
        val tempList = mutableListOf<FoodItem>()
        val cursor = db.query(TableInfo2.TABLE_NAME, null, TableInfo2.TABLE_COLUMN_TYPE + " = ?", arrayOf("2"), null, null, null)
        // 遍历所有符合条件的记录转成实体类
        while (cursor.moveToNext()) {
            val item = FoodItem(
                type = cursor.getString(1),
                name = cursor.getString(2),
                desc = cursor.getString(3),
                imgUri = cursor.getString(5)
            )
            tempList.add(item)
        }
        cursor.close() // 必须关闭Cursor避免内存泄漏
        itemList = tempList
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int = itemList.size

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val currentItem = itemList[position]
        // 直接读取对应位置的实体类数据即可
        if(currentItem.type == "option") {
            holder.imgM.setImageURI(currentItem.imgUri.toUri())
            holder.imgM.setOnClickListener {
                val intent = Intent(context, AddNewFood::class.java)
                context.startActivity(intent)
            }
        } else {
            holder.imgM.setImageURI(currentItem.imgUri.toUri())
            holder.imgM.setOnClickListener {
                val dialog = DialogPopUp(currentItem.type, currentItem.name, currentItem.desc, currentItem.imgUri.toUri())
                val manager = (context as AppCompatActivity).supportFragmentManager
                dialog.show(manager, "customDialog")
            }
            holder.imgM.setOnLongClickListener(object: View.OnLongClickListener{
                override fun onLongClick(p0: View?): Boolean {
                    MainActivity.position = position + 1
                    MainActivity.check2 = true
                    val intent = Intent(context, AddNewFood::class.java)
                    context.startActivity(intent)
                    return false
                }
            })
        }
    }
}
  1. 在Activity/Fragment初始化Adapter后调用adapter.loadDataFromDB()加载数据即可,后续数据变化时重新调用该方法刷新。

临时兼容方案(不推荐)

如果你不想改动现有逻辑,只需要将onBindViewHolder中的cursor.moveToFirst()改成cursor.moveToPosition(position),用完及时关闭Cursor即可:

override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    val cursor = db.query(TableInfo2.TABLE_NAME, null, TableInfo2.TABLE_COLUMN_TYPE+" = ?", arrayOf("2"), null, null, null)
    // 直接定位到当前position对应的记录位置
    if(cursor.moveToPosition(position)) {
        // 原有逻辑不变
        if(cursor.getString(1)=="option")
        {
            holder.imgM.setImageURI(cursor.getString(5).toUri())
            holder.imgM.setOnClickListener {
                val intent = Intent(context, AddNewFood::class.java)
                context.startActivity(intent)
            }
        }else{
            holder.imgM.setImageURI(cursor.getString(5).toUri())
            holder.imgM.setOnClickListener {
                val dialog = DialogPopUp(cursor.getString(1),cursor.getString(2),cursor.getString(3),cursor.getString(5).toUri())
                val manager = (context as AppCompatActivity).supportFragmentManager
                dialog.show(manager, "customDialog")
            }
            holder.imgM.setOnLongClickListener(object: View.OnLongClickListener{
                override fun onLongClick(p0: View?): Boolean {
                    MainActivity.position = position + 1
                    MainActivity.check2 = true
                    val intent = Intent(context, AddNewFood::class.java)
                    context.startActivity(intent)
                    return false
                }
            })
        }
    }
    cursor.close() // 必须关闭
}

其他注意事项

  • 你之前按ID查询可以正常使用,是因为每次查询仅返回对应ID的单条记录,调用moveToFirst自然能拿到正确数据
  • 如果TableInfo2.TABLE_COLUMN_TYPE是数值类型,不需要用LIKE,直接用=匹配性能更高
  • 所有Cursor使用完成后必须调用close()释放资源,否则会触发内存泄漏

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 05:51:01