![](/img/trans.png)
[英]I can't delete item in recyclerview using room database and coroutines
[英]How can Room database update my RecyclerView in the UI when I have added a new item to the database using Coroutines?
我正在使用 Android 架构组件来创建一个 todo android 应用程序,该应用程序将包含一个注释列表。 每条笔记都会有一个待办事项列表,类似于 Google Keep Notes 的工作方式。 因此,您可以编辑便笺、更新待办事项的内容、添加更多待办事项,当您完成一个待办事项时,您单击复选框,它就会从待办事项列表中消失并出现在已完成的列表中。 我正在使用 Room 数据库,并使用带有 Hilt 的依赖项注入将我的存储库注入到 viewModel。 我的问题是,当我尝试在便笺中创建新的待办事项时,它会在房间中创建,因为 DAO 在协程中执行该操作,但是即使我的 recyclerview 从房间数据库获取实时数据,我的 recyclerView 也永远不会更新。 有什么我可能会丢失的吗? (我的问题出在 ToDoListViewModel 和 ToDoListFragment 中)
这是我的存储库的链接。 https://bitbucket.org/fco2/chuka-notes/src/main_feature/
我试图调试并找出我做错了什么,或者我正在做的事情是否可能是不可能的,但我相信这是可能的,但我可能只是在我的步骤中遗漏了一些东西。
提前感谢任何愿意查看并提供反馈的人!
以下是一些代码片段: ToDoListFragment.kt
@AndroidEntryPoint
class ToDoListFragment : Fragment() {
private lateinit var binding: FragmentToDoListBinding
private val viewModel: ToDoListViewModel by viewModels()
private val args: ToDoListFragmentArgs by navArgs()
private lateinit var toDoListAdapter: ToDoListAdapter
//private lateinit var completedListAdapter: ToDoCompletedListAdapter
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentToDoListBinding.inflate(layoutInflater)
binding.addToDoItemBtn.supportImageTintList = ContextCompat.getColorStateList(requireContext(), R.color.colorWhite)
binding.toDoTextTitle.setText(args.noteTitle)
initializeToDoListRecyclerView()
binding.addToDoItemBtn.setOnClickListener { addToDoNote() }
performBackPressedAction()
binding.lifecycleOwner = viewLifecycleOwner
return binding.root
}
private fun initializeToDoListRecyclerView(){
val manager = LinearLayoutManager(requireContext())
toDoListAdapter = ToDoListAdapter(ToDoListClickListener { toDoItem, view ->
performViewClickAction(toDoItem, view)
})
binding.toDoListRecyclerView.apply{
layoutManager = manager
adapter = toDoListAdapter
}
if(args.noteId == -1L)
viewModel.addToToDoList()
viewModel.toDoList().observe(viewLifecycleOwner, { updatedList ->
toDoListAdapter.differ.submitList(updatedList)
setProgressBarAndDividerVisibility(updatedList)
Timber.d("CHUKA - todo _> actual list observer size ${updatedList.size}")
})
//viewModel.resetDb()
}
private fun setProgressBarAndDividerVisibility(updatedList: List<ToDoItem>) {
binding.progressBar.visibility = View.GONE
if (updatedList.isNotEmpty())
binding.divider.visibility = View.VISIBLE
else
binding.divider.visibility = View.GONE
}
private fun addToDoNote(){
if(args.noteId == -1L){
viewModel.addToToDoList()
}else{
saveNoteAndToDoItem()
}
}
private fun saveNoteAndToDoItem(){
val note = Note().apply{
title = binding.toDoTextTitle.text.toString()
label = "Android"
id = args.noteId
}
val toDoItem = ToDoItem().apply {
isCompleted = viewModel.currentIsCompleted.value!!
message = viewModel.currentTextEdit.value!!
}
if( viewModel.currentToDoItemId.value != null && viewModel.currentToDoItemId.value != -1L)
toDoItem.id = viewModel.currentToDoItemId.value!!
//not setting noteId here because if you are creating a new note, you need to get the notId after it is created
viewModel.saveNoteAndToDoItem(note, toDoItem)
}
}
Note.kt 和 ToDoItem.kt
@Entity(tableName = "note")
data class Note(
var title: String = "<No Title>",
var label: String = "",
var color: String = "",
@ColumnInfo(name = "last_updated")
var lastUpdated: Date? = null
) {
@PrimaryKey(autoGenerate = true)
var id: Long? = null
}
@Entity(tableName = "to_do_item")
data class ToDoItem(
@ColumnInfo(name = "note_id")
var noteId: Long = 0L,
var message: String = "",
@ColumnInfo(name = "is_completed")
var isCompleted: Boolean = false,
@ColumnInfo(name = "date_completed")
var dateCompleted: Date? = null
){
@PrimaryKey(autoGenerate = true)
var id: Long? = null
}
ToDoListViewModel.kt
class ToDoListViewModel @ViewModelInject constructor(
@Assisted val savedStateHandle: SavedStateHandle,
private val repository: NoteAndToDoRepository
) : ViewModel(){
var noteId : Long? = savedStateHandle.get<Long?>("noteId")!!
fun toDoList() = repository.getAllToDoItemsFor(noteId!!).asLiveData()
//val completedList : LiveData<MutableList<ToDoItem>> = repository.getAllCompletedItemsFor(noteId.value!!)
private val _currentTextEdit = MutableLiveData<String>()
val currentTextEdit: LiveData<String> = _currentTextEdit
private val _currentIsCompleted = MutableLiveData<Boolean>()
val currentIsCompleted : LiveData<Boolean> = _currentIsCompleted
private val _currentToDoItemId = MutableLiveData<Long>()
val currentToDoItemId : LiveData<Long> = _currentToDoItemId
fun setCurrentTextEdit(s: String){
_currentTextEdit.value = s
}
fun setCurrentIsCompleted(t: Boolean){
_currentIsCompleted.value = t
}
fun setCurrentToDoItemId(l: Long){
_currentToDoItemId.value = l
}
private val _savedNote = MutableLiveData<Boolean>()
val savedNote : LiveData<Boolean> = _savedNote
init{
_currentTextEdit.value = ""
_currentIsCompleted.value = false
}
fun setSavedNote(state: Boolean){
_savedNote.postValue(state)
}
fun addToToDoList() {
viewModelScope.launch {
//if
if (noteId == -1L || noteId == null) {
noteId = repository.upsertNote(Note())
Timber.d("CHUKA - todo _> Note is NULL")
}
val itemToAdd = ToDoItem(noteId!!)
Timber.d("CHUKA - todo _> itemToAdd: $itemToAdd")
_currentToDoItemId.value = repository.upsertToDoItem(itemToAdd) // this is what is supposed to trigger recyclerview update.
}
}
fun saveNoteAndToDoItem(note: Note, toDoItem: ToDoItem) = viewModelScope.launch {
//Timber.d("CHUKA - todo _> called saveNoteAndToDoItem , noteId: ${note.id} todoId: ${toDoItem.id}")
//save note
noteId = repository.upsertNote(note)
//save toDoItem || note that to-do id is still probably null here
//update noteId for toDoItem
toDoItem.noteId = noteId!!
repository.upsertToDoItem(toDoItem)
Timber.d("CHUKA - todo _> noteId: $noteId, -- ${note}, -- $toDoItem")
setCurrentTextEdit("")
setCurrentToDoItemId(currentToDoItemId.value!!)
}
}
NoteAndToDoItemDao.kt
@Dao
interface NoteAndToDoItemDao {
//get all notes -
@Transaction
@Query("SELECT * FROM note")
fun getAllNotes(): LiveData<List<NoteWithToDoItem>>
//insert note
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsertNote(note: Note): Long
//get all to-do items
@Query("SELECT * FROM to_do_item WHERE note_id = :noteId AND is_completed = 0")
fun getAllToDoItemsFor(noteId: Long): Flow<List<ToDoItem>>
//upsert to-do item
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun upsertToDoItem(toDoItem: ToDoItem): Long
}
我的应用模块
@Module
@InstallIn(ApplicationComponent::class)
object ChukaNotesAppModule {
@Provides
@Singleton
fun getDatabase(@ApplicationContext context: Context) = Room.databaseBuilder(
context, ToDoItemsDatabase::class.java, DATABASE_NAME
).build()
//.addMigrations(MIGRATION_1_2)
@Provides
@Singleton
fun getNoteAndToDoItemDao(database: ToDoItemsDatabase) = database.getToDoItemsDao()
//Room Migrations
private val MIGRATION_1_2: Migration = object : Migration(1, 2) {
override fun migrate(database: SupportSQLiteDatabase) {
// Since we didn't alter the table, there's nothing else to do here.
}
}
}
ToDoListAdapter.kt
class ToDoListAdapter(private val clickListener: ToDoListClickListener)
: RecyclerView.Adapter<ToDoListAdapter.ToDoListViewHolder>() {
class ToDoListViewHolder(private var binding: ComponentToDoItemBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(toDoItem: ToDoItem, clickListener: ToDoListClickListener) {
binding.toDoItem = toDoItem
binding.clickListener = clickListener
binding.executePendingBindings()
}
companion object{
fun from(parent: ViewGroup): ToDoListViewHolder {
val inflater = LayoutInflater.from(parent.context)
val binding = ComponentToDoItemBinding.inflate(inflater, parent, false)
return ToDoListViewHolder(binding)
}
}
}
private val differCallback = object: DiffUtil.ItemCallback<ToDoItem>(){
override fun areItemsTheSame(oldItem: ToDoItem, newItem: ToDoItem): Boolean {
Timber.d("CHUKA - todo _> areItemsTheSame")
return oldItem.message == newItem.message && oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: ToDoItem, newItem: ToDoItem): Boolean {
Timber.d("CHUKA - todo _> areContentsTheSame")
return oldItem.hashCode() == newItem.hashCode()
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ToDoListViewHolder {
return ToDoListViewHolder.from(parent)
}
override fun onBindViewHolder(holder: ToDoListViewHolder, position: Int) {
val toDoItem = differ.currentList[position]!!
holder.bind(toDoItem, clickListener)
}
val differ = AsyncListDiffer(this, differCallback)
override fun getItemCount(): Int = differ.currentList.size
}
class ToDoListClickListener(val clickListener: (ToDoItem, View) -> Unit){
fun onClick(toDoItem: ToDoItem, view: View) = clickListener(toDoItem, view)
}
我能够使用 Kotlin Flow 解决上述问题,然后使用 Flow collect function 在 viewModel 中监听结果。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.