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

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
}
}
问题原因与修复方案

核心问题

  1. 数据源重复累加:getNotesFromDatabase方法中,collect会持续监听数据库的Flow数据流,每次数据库有更新(比如新增条目),都会把所有数据追加到同一个notesList中,没有先清空旧数据,导致列表内容重复。
  2. 重复触发更新: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 21:21:36