首页 > 解决方案 > 如何使用 Room 在片段上更新 Livedata 对象?

问题描述

我对 MVVM 架构还很陌生,我有点困惑,为什么我似乎无法让 Livedata 对象更新到片段中的 recyclerview 中。在整个应用程序的各个位置使用 Log,我发现 sqlite 表正在更新,它只是不显示数据。

这是一个简单的笔记应用程序,我现在只是试图显示 sqlite 表中的标题。我可以在以后担心其余的事情。

任何帮助表示赞赏,我是房间的新手(一般是 sqlite),所以很多代码都是基于Room With a View教程。

如有必要,我可以提供更多代码,我只是想让它尽可能简单,以便有人查看!

标题片段

import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class TitlesFragment : Fragment(){

    private lateinit var noteViewModel: NoteViewModel
    private lateinit var note: Note

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val rootView = inflater.inflate(R.layout.fragment_titles, container, false)
        val adapter = this.context?.let { NoteListAdapter(it) }
        noteViewModel = ViewModelProvider(this).get(NoteViewModel::class.java)
        noteViewModel.allNotes.observe(viewLifecycleOwner, Observer { notes ->
            notes?.let { adapter?.setNotes(it) }
        })
        val recyclerView = rootView.findViewById< RecyclerView>(R.id.titlesrecyclerview) as RecyclerView

        recyclerView.layoutManager = LinearLayoutManager(activity)
        recyclerView.adapter = this.context?.let { NoteListAdapter(it) }

        return rootView
    }

    fun receiveNote(note: Note) {
        this.note = note
        noteViewModel.insert(this.note)
    }
}

笔记列表适配器

import android.content.Context
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.lifecycle.LiveData
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.RecyclerView

class NoteListAdapter internal constructor(
    context: Context
) : RecyclerView.Adapter<NoteListAdapter.NoteViewHolder>() {
    private val inflater: LayoutInflater = LayoutInflater.from(context)
    private var notes = emptyList<Note>()

    inner class NoteViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val noteItemView: TextView = itemView.findViewById(R.id.title_box)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NoteViewHolder {
        val itemView = inflater.inflate(R.layout.fragment_titles, parent, false)
        return NoteViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: NoteViewHolder, position: Int) {
        val current = notes[position]
        holder.noteItemView.text = current.note
    }

    internal fun setNotes(notes: List<Note>) {
        this.notes = notes
        notifyDataSetChanged()
    }

    override fun getItemCount() = notes.size
}

NoteViewModel

import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class NoteViewModel (application: Application) : AndroidViewModel(application) {

    private val repository: NoteRepository

    val allNotes: LiveData<List<Note>>

    init {
        val notesDao = NoteDatabase.getDatabase(application, viewModelScope).noteDao()
        repository = NoteRepository(notesDao)
        allNotes = repository.allNotes
    }

    fun insert(note: Note) = viewModelScope.launch(Dispatchers.IO) {
        repository.insert(note)
    }
}

注意数据库

import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

@Database(entities = arrayOf(Note::class), version = 1, exportSchema = false)
abstract class NoteDatabase : RoomDatabase() {

    abstract fun noteDao(): NoteDao

    companion object {
        @Volatile
        private var INSTANCE: NoteDatabase? = null

        fun getDatabase(context: Context, scope: CoroutineScope): NoteDatabase {
            val tempInstance = INSTANCE
            if (tempInstance != null) {
                return tempInstance
            }
            synchronized(this) {
                val instance = Room.databaseBuilder(
                    context.applicationContext,
                    NoteDatabase::class.java,
                    "note_database"
                ).addCallback(NoteDatabaseCallback(scope)).build()

                INSTANCE = instance
                return instance
            }
        }
    }

    private class NoteDatabaseCallback(
        private val scope: CoroutineScope) : RoomDatabase.Callback() {
        override fun onOpen(db: SupportSQLiteDatabase) {
            super.onOpen(db)

            INSTANCE?.let { database ->
                scope.launch {
                    populateDatabase(database.noteDao())
                }
            }
        }

        suspend fun populateDatabase(noteDao: NoteDao) {
            noteDao.deleteAll()

            var note = Note("test1", "hello")
            noteDao.insert(note)
            note = Note("test2", "world")
            noteDao.insert(note)
        }
    }
}

注意存储库

import androidx.lifecycle.LiveData

class NoteRepository(private val noteDao: NoteDao) {
    val allNotes: LiveData<List<Note>> = noteDao.getAllNotes()

    suspend fun insert(note: Note) {
        noteDao.insert(note)
    }
}

笔记道

import androidx.lifecycle.LiveData
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query

@Dao
interface NoteDao {
    @Query("SELECT * from note_table")
    fun getAllNotes(): LiveData<List<Note>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(note: Note)

    @Query("DELETE FROM note_table")
    suspend fun deleteAll()
}

标签: androidandroid-fragmentskotlinandroid-roomandroid-livedata

解决方案


看完后onCreateView()你有:

val adapter = this.context?.let { NoteListAdapter(it) }

并且观察者正在尝试相应地更新:

noteViewModel.allNotes.observe(viewLifecycleOwner, Observer { notes ->
    notes?.let { adapter?.setNotes(it) }
})

但是,您要在以下位置设置新适配器:

recyclerView.adapter = this.context?.let { NoteListAdapter(it) }

因此它不会填充正确的适配器,因此请考虑切换到实际adapter并删除新分配。

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    val rootView inflater.inflate(R.layout.fragment_titles, container, false)
    noteViewModel = ViewModelProvider(this).get(NoteViewModel::class.java)
    val adapter = NoteListAdapter(requireContext())
    noteViewModel.allNotes.observe(viewLifecycleOwner, Observer { notes ->
        notes?.let { adapter.setNotes(it) }
    })
    val recyclerView = rootView.findViewById< RecyclerView>(R.id.titlesrecyclerview) as RecyclerView

    recyclerView.layoutManager = LinearLayoutManager(requireContext())
    recyclerView.adapter = adapter

    return rootView
}

推荐阅读