如何在不刷新活动的情况下更新Kotlin中的Room数据库?

时间:2019-07-26 14:27:46

标签: android kotlin android-room

我对Kotlin / Android开发还很陌生,并且正在尝试找出更新Room数据库中数据的最佳方法。在学习了一些教程之后,我目前拥有一个看起来像这样的体系结构:

具有表和DAO的房间数据库->存储库-> ViewModel->活动

因此,活动具有一个调用存储库的ViewModel,该存储库又会更新数据库。

该活动的ViewModel包含一个对象的LiveData列表(还有一个工厂可以创建ViewModel,但这只是为了允许bookId传入):

class ViewBookViewModel(application: Application, bookId: Int) : AndroidViewModel(application) {
    private val repository: AppRepository
    internal val flashCards: LiveData<List<FlashCard>>

    init {
        val flashCardDao = AppDatabase.getDatabase(application, viewModelScope).flashCardDao()
        val bookDao = AppDatabase.getDatabase(application, viewModelScope).bookDao()
        repository = AppRepository(flashCardDao, bookDao)
        flashCards = flashCardDao.getByBookId(bookId)
    }

    fun insert(flashCard: FlashCard) = viewModelScope.launch(Dispatchers.IO){
        repository.insert(flashCard)
    }

    fun setIsFavorited(cardUid: Long, favorited: Boolean) = viewModelScope.launch(Dispatchers.IO) {
        repository.setIsFavorited(cardUid, favorited)
    }
}

//The actual query that gets called eventually
    @Query("UPDATE flashcard SET is_favorited = :favorited WHERE uid LIKE :cardUid")
fun setFavorited(cardUid: Long, favorited: Boolean)

然后Activity设置viewModel并在

上创建一个观察者
class ViewBookActivity : AppCompatActivity() {
    private lateinit var flashCards: LiveData<List<FlashCard>>
    private var layoutManager: RecyclerView.LayoutManager? = null
    private lateinit var viewModel: ViewBookViewModel
    private var bookId: Int = 0
    private lateinit var bookTitle: String

    override fun onCreate(savedInstanceState: Bundle?) {
...
        bookId = intent.extras["bookId"] as Int
        bookTitle = intent.extras["bookTitle"].toString()

        layoutManager = LinearLayoutManager(this)
        flashCardRecyclerView.layoutManager = layoutManager

        viewModel = ViewModelProviders.of(this, ViewBookViewModelFactory(application, bookId as Int)).get(ViewBookViewModel::class.java)

        flashCards = viewModel.flashCards
        flashCards.observe(this, Observer { flashCards:List<FlashCard> ->
                flashCardRecyclerView.adapter = FlashCardRecyclerAdapter(flashCards, viewModel)
            })
    }
}

最后,我有一个自定义RecyclerAdapter,这是我遇到麻烦的地方。我进行了设置,以便当用户点击闪存卡上的“收藏夹”按钮时,它将更新数据库。但是,这也会导致活动“刷新”,滚动到顶部。我认为这是因为它正在观察LiveData,并且正在更改数据。

带有ViewHolder代码的自定义RecylcerAdapter(剥离的无关代码):

class FlashCardRecyclerAdapter(val flashCards: List<FlashCard>, val viewModel: ViewBookViewModel) : RecyclerView.Adapter<FlashCardRecyclerAdapter.FlashCardViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FlashCardViewHolder {
        val v: View = LayoutInflater
            .from(parent.context)
            .inflate(R.layout.flash_card, parent, false)
        return FlashCardViewHolder(v)
    }

    override fun onBindViewHolder(holder: FlashCardViewHolder, position: Int) {
        val card = flashCards[position]
        holder.isFavorited = card.isFavorited
        holder.uid = card.uid
        holder.modifyFavoriteButtonImage(holder.isFavorited)
    }

    override fun getItemCount(): Int {
        return flashCards.size
    }

    inner class FlashCardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
        var mFavorited: Button

        var frontShowing: Boolean
        var isFavorited: Boolean = false
        var uid: Long = 0

        init {
            mFavorited = itemView.findViewById(R.id.favoriteButton)

            mFavorited.setOnClickListener { _ ->
                isFavorited = !isFavorited

                viewModel.setIsFavorited(uid, isFavorited) // Here is the database call

                modifyFavoriteButtonImage(isFavorited)
            }
        }

        fun modifyFavoriteButtonImage(isFavorited: Boolean){
            // Code removed, just updates the image to be a filled/empty star based on favorited status
    }
}

我觉得我可能做错了,因为将ViewModel传递到recylcer适配器中以更新数据库似乎不正确。我是否应该针对这种情况使用某种模式,还是应该将代码更改为不使用LiveData?任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

flashCards.observe(this, Observer { flashCards:List<FlashCard> ->
                flashCardRecyclerView.adapter = FlashCardRecyclerAdapter(flashCards, viewModel)
            }

您不应该在此处创建新的适配器实例,而是将从实时数据中获取的值分配给现有适配器(adapter.flashCards = flashCards,LiveData值)并调用{{1 }},这将告诉您的适配器有新数据进入,需要更新。

您不应将ViewModel传递给适配器(或其他任何东西)。

您可以改为执行以下操作:

adapter.notifyDataSetChanged

然后,在声明适配器的位置执行以下操作:

class FlashCardRecyclerAdapter(val flashCards: List<FlashCard>, val callback:(FlashCard) -> Unit)

然后:

val adapter = FlashCardRecyclerAdapter(...) {
  viewModel.update(it)
}

答案 1 :(得分:0)

在您的存储库方法中,我不确定您在做什么,但是应该传入livedata实例的基础数据,而不是传入livedata实例。这样,每次调用setIsFavorited()时,主活动中的观察者都不会触发。如果确实要触发观察者,则只需在livedata实例上调用postValue()即可。至于适配器问题,我不知道最佳实践,但是我通常创建一个侦听器接口,因此不必到处传递我的视图模型。我所有的视图模型都包含在我的片段中,再也没有其他地方了。让我知道这是否回答了您的问题。

此外,如果您将视图模型与recyclerview一起使用,请考虑使用列表适配器。使它们可以与视图模型无缝协作。 https://developer.android.com/reference/android/support/v7/recyclerview/extensions/ListAdapter

这使得将视图模型与recyclerview一起使用变得更加简单。