【问题标题】:How to update Room database in Kotlin without activity refreshing?如何在不刷新活动的情况下更新 Kotlin 中的房间数据库?
【发布时间】:2019-07-26 14:27:46
【问题描述】:

我是 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,这是我遇到麻烦的地方。我进行了设置,以便当用户点击闪存卡上的“收藏夹”按钮时,它会更新数据库。但是,这也会导致 Activity “刷新”,滚动到顶部。我认为这是因为它正在观察 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 传递到 recycler 适配器以更新数据库似乎不正确。在这种情况下我应该使用一种模式,还是应该将代码更改为不使用 LiveData?任何帮助将不胜感激。

【问题讨论】:

  • 你不应该在 Observer 中分配 recyclerview 适配器,你应该只将你的闪存卡更新为一个数组。
  • 您的架构理念是正确的:Room Database with tables and DAOs -&gt; Repository -&gt; ViewModel -&gt; Activity
  • 嗨,OP,看看我的回答,看看是否有帮助,如果您有任何问题或希望我解释,请随时告诉我:D

标签: android kotlin android-room


【解决方案1】:
flashCards.observe(this, Observer { flashCards:List<FlashCard> ->
                flashCardRecyclerView.adapter = FlashCardRecyclerAdapter(flashCards, viewModel)
            }

你不应该在这里创建一个新的适配器实例,而是将你从实时数据中获得的值分配给现有的适配器(adapter.flashCards = flashCards,LiveData 值)并调用adapter.notifyDataSetChanged,这会告诉你的适配器,新数据进来了,它需要更新。

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

你可以这样做:

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

然后,在你声明你的适配器的地方,你这样做:

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

然后:

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

        holder.modifyFavoriteButtonImage(holder.isFavorited)
    }

【讨论】:

  • 谢谢,我已经实现了回调,而不是直接传递 ViewModel。我现在面临的唯一问题是,当我初始化 FlashCardRecyclerAdapter 时,ViewModel 中的闪存卡列表尚未初始化。所以adapter = FlashCardRecyclerAdapter(viewModel.flashCards.value){viewModel.update(it)} 不起作用,因为viewModel.flashCards.value 为空
  • 更新:我更新了我的RecyclerAdapter 以获取一个可以为空的列表并对其添加了一些检查,现在它似乎可以正常工作了。
  • 太棒了,乐于助人:D
【解决方案2】:

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

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

它使得使用带有 recyclerview 的视图模型变得更加简单。

【讨论】:

  • 谢谢你,我会看看你的建议!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多