Android RecyclerView Adapter中Cursor查询结果无法遍历仅显示单条记录怎么办
问题根因
onBindViewHolder 会为每个列表项单独执行,你当前的写法每次执行都会重新查询全量符合 type=2 条件的记录,但是每次仅调用 cursor.moveToFirst() 定位到第一条记录,因此无论当前绑定的是第几个列表位置,永远只会读取第一条数据,和你加不加 moveToNext 没有关系。
最优解决方案(推荐)
不要在 onBindViewHolder 中执行数据库查询,该方法会频繁调用,频繁查询数据库会严重降低列表滑动性能。你应该提前将符合条件的数据一次性查出来转换成实体类列表,交由Adapter持有使用:
- 在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 } }) } } }
- 在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
相关产品推荐
相关产品推荐

