Как заставить объект Livedata обновлять фрагмент с помощью Room? - PullRequest
0 голосов
/ 08 мая 2020

Я новичок в архитектуре MVVM, и я немного смущен тем, почему я не могу заставить объект Livedata обновляться до recyclerview во фрагменте. Используя журнал в разных местах приложения, я обнаружил, что таблица sqlite обновляется, она просто не отображает данные.

Это простое приложение для создания заметок, и я только пытаюсь отобразить заголовки из таблицы sqlite в данный момент. Об остальном я могу побеспокоиться позже.

Любая помощь приветствуется, я новичок в комнате (и sqlite в целом), поэтому большая часть кода основана на Room With a View

Я могу предоставить больше кода, если необходимо, я просто стараюсь сделать его как можно более простым, чтобы кто-то посмотрел!

TitlesFragment

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)
    }
}

NoteListAdapter

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)
    }
}

NoteDatabase

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)
        }
    }
}

NoteRepository

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)
    }
}

NoteDao

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()
}

1 Ответ

1 голос
/ 08 мая 2020

Посмотрев на 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
}
...