我对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?任何帮助将不胜感激。
答案 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一起使用变得更加简单。