RecyclerView适配器报错:两个ViewHolder稳定ID重复,求解决方案
解决方案
核心问题在于:队列中重复的歌曲共享同一个songId,导致getItemId返回的ID不唯一,RecyclerView无法区分不同的列表项,进而引发状态更新异常。下面给出两种可行方案:
方案一:用组合模式创建独立队列条目(推荐)
放弃继承Song,转而创建一个专门的QueueItem类,包装歌曲信息、唯一标识和播放状态,彻底区分队列中的每个独立条目:
// 定义队列条目类,包含唯一ID、歌曲本体和播放状态 data class QueueItem( val uniqueId: Long, // 每个队列条目的全局唯一ID val song: Song, val isPlaying: Boolean )
实现细节:
- 生成唯一ID:
- 本地队列可维护一个自增计数器,每次添加歌曲时递增赋值(比如
var idCounter = 0L; val newId = idCounter++) - 也可用UUID生成:
UUID.randomUUID().mostSignificantBits(取Long部分,冲突概率极低)
- 本地队列可维护一个自增计数器,每次添加歌曲时递增赋值(比如
- 适配器修改:
数据集改为List<QueueItem>,并在getItemId中返回唯一ID:override fun getItemId(position: Int): Long { return dataset[position].uniqueId }
这种方式用组合替代继承,灵活性更高,避免了父类结构变化带来的维护问题,同时保证每个列表项ID绝对唯一。
方案二:修正QueueSong的实现(若坚持继承)
如果必须继承Song,需确保queueId是真正唯一的标识,而非依赖hashCode(hashCode存在重复风险):
data class QueueSong( private val song: Song, // 初始化时生成唯一ID,避免hashCode重复问题 val queueId: Long = UUID.randomUUID().mostSignificantBits ) : Song(song) { // 若Song是data class,建议重写equals和hashCode,仅以queueId作为判断依据 override fun equals(other: Any?): Boolean { if (this === other) return true if (javaClass != other?.javaClass) return false other as QueueSong return queueId == other.queueId } override fun hashCode(): Int { return queueId.hashCode() } }
然后适配器的getItemId返回queueId:
override fun getItemId(position: Int): Long { return dataset[position].queueId }
额外注意事项
- 确保
setHasStableIds(true)在适配器初始化时仅调用一次 - 更新播放状态时,修改对应队列条目的
isPlaying属性后,调用notifyItemChanged(position)而非notifyDataSetChanged,提升性能并避免列表闪烁
内容的提问来源于stack exchange,提问作者pandey_shubham
相关产品推荐
相关产品推荐

