RecyclerView添加条目后列表重复,notifyDataSetChanged无效问题求助
问题描述
我尝试制作一个展示条目列表的RecyclerView,通过另一个Activity添加新条目,但更新RecyclerView时,列表内容不断重复,新条目被添加到底部。调用notifyDataSetChanged()后问题依然存在。

Adapter代码
import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.StaggeredGridLayoutManager import com.example.notap.databinding.NotesLayoutBinding class NotesAdapter(private val notes : ArrayList<Notes>) : RecyclerView.Adapter<NotesAdapter.ViewHolder>() { class ViewHolder(binding : NotesLayoutBinding) : RecyclerView.ViewHolder(binding.root){ val noteTitle = binding.tvNoteTitle val noteBody = binding.tvNoteBody val rvTagsInNotePreview = binding.rvTagsInNotePreview val llTagsDisplay = binding.llTAgsDisplay } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder(NotesLayoutBinding.inflate(LayoutInflater.from(parent.context),parent,false)) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val item = notes[position] holder.noteTitle.text = item.title holder.noteBody.text = item.noteBody if (setTagsInNotePreview(holder,item.tags)) holder.llTagsDisplay.visibility = View.VISIBLE } override fun getItemCount(): Int { return notes.size } private fun setTagsInNotePreview(holder : ViewHolder, tags : String) : Boolean{ if (tags.isNotEmpty()){ val tagsObjects = arrayListOf<TagsObject>() val tagsListString = tags.split(" || ") tagsListString.forEach { if (it.isNotEmpty()) tagsObjects.add(TagsObject(it)) } val tagsAdapter = TagsAdapter(tagsObjects) holder.rvTagsInNotePreview.apply { adapter = tagsAdapter layoutManager = StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.HORIZONTAL) } return true } return false } }
MainActivity代码
class MainActivity : AppCompatActivity() { private var IS_NOTES_RADIO_BUTTON_SELECTED = true private var isNoteAdded = false private lateinit var notesListFromDatabase : ArrayList<Notes> private lateinit var notesAdapter : NotesAdapter private lateinit var noteDao : NoteDao companion object{ private const val ADD_NEW_NOTE_REQUEST_CODE = 1 private const val ADD_NEW_FILE_REQUEST_CODE = 2 } private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) noteDao = (application as NoteApp).db.noteDao() notesListFromDatabase = getNotesFromDatabase(noteDao) notesAdapter = NotesAdapter(notesListFromDatabase) setNotesInRecyclerView(notesAdapter) binding.btnAddNote.setOnClickListener { val intent = Intent(this,AddNewNoteActivity::class.java) startActivityForResult(intent, ADD_NEW_NOTE_REQUEST_CODE) } } @SuppressLint("NotifyDataSetChanged") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if(resultCode == Activity.RESULT_OK){ when(requestCode){ ADD_NEW_NOTE_REQUEST_CODE -> { Toast.makeText(this, "Note Added To Database", Toast.LENGTH_SHORT).show() notesAdapter.notifyDataSetChanged() isNoteAdded = true } ADD_NEW_FILE_REQUEST_CODE -> { // to do } } }else if (resultCode == Activity.RESULT_CANCELED){ when(requestCode){ ADD_NEW_NOTE_REQUEST_CODE -> { // to do } ADD_NEW_FILE_REQUEST_CODE -> { // to do } } } } private fun setNotesInRecyclerView(notesAdapter : NotesAdapter){ binding.rvNotesListView.apply { adapter = notesAdapter layoutManager = LinearLayoutManager(this.context) } } private fun getNotesFromDatabase(notesDao: NoteDao) : ArrayList<Notes> { val notesList = arrayListOf<Notes>() lifecycleScope.launch { notesDao.fetchAllNotes().collect{ListOfNotes -> ListOfNotes.forEach {NoteEntity -> notesList.add(Notes(title = NoteEntity.title, tags = NoteEntity.tags, noteBody = NoteEntity.noteBody)) } } } return notesList } }
问题原因与修复方案
核心问题
- 数据源重复累加:
getNotesFromDatabase方法中,collect会持续监听数据库的Flow数据流,每次数据库有更新(比如新增条目),都会把所有数据追加到同一个notesList中,没有先清空旧数据,导致列表内容重复。 - 重复触发更新:
onActivityResult中手动调用notifyDataSetChanged(),但此时collect已经因为数据库变化自动触发了一次数据追加,双重操作加剧了重复问题。
修复步骤
步骤1:修正数据库数据加载逻辑
修改数据加载逻辑,每次收到新的数据库数据时先清空旧列表,再添加新数据,同时通知Adapter更新:
private fun setupNotesObserver() { val notesList = arrayListOf<Notes>() notesAdapter = NotesAdapter(notesList) setNotesInRecyclerView(notesAdapter) lifecycleScope.launch { noteDao.fetchAllNotes().collect { listOfNotes -> notesList.clear() // 先清空旧数据 listOfNotes.forEach { noteEntity -> notesList.add(Notes( title = noteEntity.title, tags = noteEntity.tags, noteBody = noteEntity.noteBody )) } notesAdapter.notifyDataSetChanged() // 数据更新后通知Adapter } } }
然后在onCreate中替换原来的数据源初始化代码:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) // 补充缺失的ContentView设置 noteDao = (application as NoteApp).db.noteDao() setupNotesObserver() // 替换原有的数据源加载与Adapter初始化 binding.btnAddNote.setOnClickListener { val intent = Intent(this,AddNewNoteActivity::class.java) startActivityForResult(intent, ADD_NEW_NOTE_REQUEST_CODE) } }
步骤2:移除冗余的更新调用
删除onActivityResult中ADD_NEW_NOTE_REQUEST_CODE分支里的notesAdapter.notifyDataSetChanged(),因为collect会自动监听数据库变化并更新列表:
@SuppressLint("NotifyDataSetChanged") override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if(resultCode == Activity.RESULT_OK){ when(requestCode){ ADD_NEW_NOTE_REQUEST_CODE -> { Toast.makeText(this, "Note Added To Database", Toast.LENGTH_SHORT).show() isNoteAdded = true // 移除冗余的notifyDataSetChanged()调用 } ADD_NEW_FILE_REQUEST_CODE -> { // to do } } } // 其余代码保持不变 }
步骤3:优化子RecyclerView性能(可选)
原代码中每次onBindViewHolder都会创建新的TagsAdapter和LayoutManager,可以优化为复用已有实例:
private fun setTagsInNotePreview(holder : ViewHolder, tags : String) : Boolean{ if (tags.isNotEmpty()){ val tagsObjects = arrayListOf<TagsObject>() val tagsListString = tags.split(" || ") tagsListString.forEach { if (it.isNotEmpty()) tagsObjects.add(TagsObject(it)) } // 复用已有Adapter,避免重复创建 val tagsAdapter = holder.rvTagsInNotePreview.adapter as? TagsAdapter ?: run { val adapter = TagsAdapter(tagsObjects) holder.rvTagsInNotePreview.layoutManager = StaggeredGridLayoutManager(1, StaggeredGridLayoutManager.HORIZONTAL) holder.rvTagsInNotePreview.adapter = adapter adapter } tagsAdapter.updateTags(tagsObjects) // 需要给TagsAdapter添加更新方法 holder.llTagsDisplay.visibility = View.VISIBLE return true } holder.llTagsDisplay.visibility = View.GONE // 无标签时隐藏布局 return false }
同时给TagsAdapter添加数据更新方法:
class TagsAdapter(private val tags: ArrayList<TagsObject>) : RecyclerView.Adapter<TagsAdapter.ViewHolder>() { // ... 原有代码 ... fun updateTags(newTags: List<TagsObject>) { tags.clear() tags.addAll(newTags) notifyDataSetChanged() } }
效果验证
完成以上修改后,新增条目时数据库会触发Flow更新,collect块会清空旧列表并加载最新数据,然后通知Adapter刷新,RecyclerView就能正确显示无重复的最新列表。
内容的提问来源于stack exchange,提问作者Waks hs
相关产品推荐
相关产品推荐

