Android 上的 Kotlin:如何在片段中使用数据库中的 LiveData?
Kotlin on Android: How to use LiveData from a database in a fragment?
我使用 MVVM 并在数据库中有一个数据元素列表,该列表通过 DAO 和存储库映射到 ViewModel 函数。
现在,我的问题相当平庸;我只想使用片段变量中的数据,但我发现类型不匹配。
MVVM 引入了一些代码,为了上下文的完整性,我将 运行 通读它,但我将把它剥离到最基本的部分:
数据元素属于数据class,“对象”:
@Entity(tableName = "objects")
data class Objects(
@ColumnInfo(name = "object_name")
var objectName: String
) {
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}
在ObjectsDao.kt中:
@Dao
interface ObjectsDao {
@Query("SELECT * FROM objects")
fun getObjects(): LiveData<List<Objects>>
}
我的数据库:
@Database(
entities = [Objects::class],
version = 1
)
abstract class ObjectsDatabase: RoomDatabase() {
abstract fun getObjectsDao(): ObjectsDao
companion object {
// create database
}
}
在ObjectsRepository.kt中:
class ObjectsRepository (private val db: ObjectsDatabase) {
fun getObjects() = db.getObjectsDao().getObjects()
}
在ObjectsViewModel.kt中:
class ObjectsViewModel(private val repository: ObjectsRepository): ViewModel() {
fun getObjects() = repository.getObjects()
}
在ObjectsFragment.kt中:
class ObjectsFragment : Fragment(), KodeinAware {
private lateinit var viewModel: ObjectsViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this, factory).get(ObjectsViewModel::class.java)
// I use the objects in a recyclerview; rvObjectList
rvObjectList.layoutManager = GridLayoutManager(context, gridColumns)
val adapter = ObjectsAdapter(listOf(), viewModel)
// And I use an observer to keep the recyclerview updated
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})
}
}
适配器:
class ObjectsAdapter(var objects: List<Objects>,
private val viewModel: ObjectsViewModel):
RecyclerView.Adapter<ObjectsAdapter.ObjectsViewHolder>() {
// Just a recyclerview adapter
}
现在,以上所有工作正常 - 但我的问题是我不想使用观察者来填充 recyclerview;在数据库中我存储了一些对象,但还有更多我不想存储的对象。
所以,我尝试这样做(在 ObjectsFragment 中):
var otherObjects: List<Objects>
// ...
if (condition) {
adapter.objects = viewModel.getObjects()
} else {
adapter.objects = otherObjects
}
adapter.notifyDataSetChanged()
最后,我的问题;我得到真实条件分配的类型不匹配:
Type mismatch: inferred type is LiveData<List> but List was expected
我无法理解这个问题。这不正是观察者正在发生的事情吗?我知道支持属性,例如 here 所解释的,但是当我的数据未在 ViewModel 中定义时,我不知道该怎么做。
你可以这样做:
var displayDataFromDatabase = true // Choose whatever default fits your use-case
var databaseList = emptyList<Objects>() // List we get from database
val otherList = // The other list that you want to show
toggleSwitch.setOnCheckedChangeListener { _, isChecked ->
displayDataFromDatabase = isChecked // Or the negation of this
// Update adapter to use databaseList or otherList depending upon "isChecked"
}
viewModel.getObjects.observe(viewLifecycleOwner) { list ->
databaseList = list
if(displayDataFromDatabase)
// Update adapter to use this databaseList
}
我们需要一些东西来切换数据源。我们将切换数据源事件传递给 viewModel。
mySwitch.setOnCheckedChangeListener { _, isChecked ->
viewModel.switchDataSource(isChecked)
}
在 viewModel 中我们处理切换数据源
(要使用 switchMap 包含 implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
)
class ObjectsViewModel(private val repository: ObjectsRepository) : ViewModel() {
// Best practice is to keep your data in viewModel. And it is useful for us in this case too.
private val otherObjects = listOf<Objects>()
private val _loadDataFromDataBase = MutableLiveData<Boolean>()
// In case your repository returns liveData of favorite list
// from dataBase replace MutableLiveData(otherObjects) with repository.getFavorite()
fun getObjects() = _loadDataFromDataBase.switchMap {
if (it) repository.getObjects() else MutableLiveData(otherObjects)
}
fun switchDataSource(fromDataBase: Boolean) {
_loadDataFromDataBase.value = fromDataBase
}
}
在activity/fragment观察getObjects()
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})
我使用 MVVM 并在数据库中有一个数据元素列表,该列表通过 DAO 和存储库映射到 ViewModel 函数。
现在,我的问题相当平庸;我只想使用片段变量中的数据,但我发现类型不匹配。
MVVM 引入了一些代码,为了上下文的完整性,我将 运行 通读它,但我将把它剥离到最基本的部分:
数据元素属于数据class,“对象”:
@Entity(tableName = "objects")
data class Objects(
@ColumnInfo(name = "object_name")
var objectName: String
) {
@PrimaryKey(autoGenerate = true)
var id: Int? = null
}
在ObjectsDao.kt中:
@Dao
interface ObjectsDao {
@Query("SELECT * FROM objects")
fun getObjects(): LiveData<List<Objects>>
}
我的数据库:
@Database(
entities = [Objects::class],
version = 1
)
abstract class ObjectsDatabase: RoomDatabase() {
abstract fun getObjectsDao(): ObjectsDao
companion object {
// create database
}
}
在ObjectsRepository.kt中:
class ObjectsRepository (private val db: ObjectsDatabase) {
fun getObjects() = db.getObjectsDao().getObjects()
}
在ObjectsViewModel.kt中:
class ObjectsViewModel(private val repository: ObjectsRepository): ViewModel() {
fun getObjects() = repository.getObjects()
}
在ObjectsFragment.kt中:
class ObjectsFragment : Fragment(), KodeinAware {
private lateinit var viewModel: ObjectsViewModel
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this, factory).get(ObjectsViewModel::class.java)
// I use the objects in a recyclerview; rvObjectList
rvObjectList.layoutManager = GridLayoutManager(context, gridColumns)
val adapter = ObjectsAdapter(listOf(), viewModel)
// And I use an observer to keep the recyclerview updated
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})
}
}
适配器:
class ObjectsAdapter(var objects: List<Objects>,
private val viewModel: ObjectsViewModel):
RecyclerView.Adapter<ObjectsAdapter.ObjectsViewHolder>() {
// Just a recyclerview adapter
}
现在,以上所有工作正常 - 但我的问题是我不想使用观察者来填充 recyclerview;在数据库中我存储了一些对象,但还有更多我不想存储的对象。
所以,我尝试这样做(在 ObjectsFragment 中):
var otherObjects: List<Objects>
// ...
if (condition) {
adapter.objects = viewModel.getObjects()
} else {
adapter.objects = otherObjects
}
adapter.notifyDataSetChanged()
最后,我的问题;我得到真实条件分配的类型不匹配:
Type mismatch: inferred type is LiveData<List> but List was expected
我无法理解这个问题。这不正是观察者正在发生的事情吗?我知道支持属性,例如 here 所解释的,但是当我的数据未在 ViewModel 中定义时,我不知道该怎么做。
你可以这样做:
var displayDataFromDatabase = true // Choose whatever default fits your use-case
var databaseList = emptyList<Objects>() // List we get from database
val otherList = // The other list that you want to show
toggleSwitch.setOnCheckedChangeListener { _, isChecked ->
displayDataFromDatabase = isChecked // Or the negation of this
// Update adapter to use databaseList or otherList depending upon "isChecked"
}
viewModel.getObjects.observe(viewLifecycleOwner) { list ->
databaseList = list
if(displayDataFromDatabase)
// Update adapter to use this databaseList
}
我们需要一些东西来切换数据源。我们将切换数据源事件传递给 viewModel。
mySwitch.setOnCheckedChangeListener { _, isChecked ->
viewModel.switchDataSource(isChecked)
}
在 viewModel 中我们处理切换数据源
(要使用 switchMap 包含 implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.4.0"
)
class ObjectsViewModel(private val repository: ObjectsRepository) : ViewModel() {
// Best practice is to keep your data in viewModel. And it is useful for us in this case too.
private val otherObjects = listOf<Objects>()
private val _loadDataFromDataBase = MutableLiveData<Boolean>()
// In case your repository returns liveData of favorite list
// from dataBase replace MutableLiveData(otherObjects) with repository.getFavorite()
fun getObjects() = _loadDataFromDataBase.switchMap {
if (it) repository.getObjects() else MutableLiveData(otherObjects)
}
fun switchDataSource(fromDataBase: Boolean) {
_loadDataFromDataBase.value = fromDataBase
}
}
在activity/fragment观察getObjects()
viewModel.getObjects.observe(viewLifecycleOwner, {
adapter.objects = it
adapter.notifyDataSetChanged()
})