Android 上的 Kotlin:如何在片段中使用数据库中的 LiveData?
Posted
技术标签:
【中文标题】Android 上的 Kotlin:如何在片段中使用数据库中的 LiveData?【英文标题】:Kotlin on Android: How to use LiveData from a database in a fragment? 【发布时间】:2021-12-19 10:55:47 【问题描述】:我使用 MVVM 并在数据库中有一个数据元素列表,该列表通过 DAO 和存储库映射到 ViewModel 函数。
现在,我的问题相当平庸;我只想使用片段变量中的数据,但我得到类型不匹配。
MVVM 引入了一些代码,为了上下文的完整性,我将运行它,但我将把它剥离到基本部分:
数据元素属于数据类“对象”:
@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
现在,以上所有操作都很好——但我的问题是我不想使用观察者来填充回收视图;在数据库中我存储了一些对象,但还有更多我不想存储的对象。
所以,我尝试这样做(在 ObjectsFragment 中):
var otherObjects: List<Objects>
// ...
if (condition)
adapter.objects = viewModel.getObjects()
else
adapter.objects = otherObjects
adapter.notifyDataSetChanged()
最后,我的问题;我得到真实条件分配的类型不匹配:
类型不匹配:推断类型为 LiveData 但应为 List
我无法理解这一点。这不是观察者身上发生的事情吗?我知道支持属性,例如解释 here,但是当我的数据未在 ViewModel 中定义时,我不知道该怎么做。
【问题讨论】:
您想根据条件过滤从数据库返回的列表。对吗? 不,我有时想从数据库中填充recyclerview,有时我想用其他一些数据填充它。例如,有时我想查看我保存在数据库中的收藏文件,有时我想查看我从文件系统中读取的所有文件;是同一个recyclerview,但是我从不同的地方获取数据。 这是一次性的事情还是可以改变?我的意思是,您是否决定了列表一次并且在应用程序上线之前不更改它,或者您是否在这些列表之间切换? 我切换 - 通常通过切换切换按钮;有时您想查看收藏夹,有时您想查看所有内容。 【参考方案1】:我们需要一些东西来切换数据源。我们将切换数据源事件传递给 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
在活动/片段中观察getObjects()
viewModel.getObjects.observe(viewLifecycleOwner,
adapter.objects = it
adapter.notifyDataSetChanged()
)
【讨论】:
【参考方案2】:你可以这样做:
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 中,以便它们在配置更改后继续存在。 我尝试了,但我觉得我有什么问题?首先,我必须为 databaseList (= emptyListviewModel.getObjects(viewLifeCyclerOwner)
而不是viewModel.getObjects.observe(viewLifecycleOwner)
。 3.databaseList
是var
。 4. 不,我们没有将 LiveData 分配给 List,我们将实时数据(这是一个列表)的值分配给 databaseList
以上是关于Android 上的 Kotlin:如何在片段中使用数据库中的 LiveData?的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 Kotlin 从 Android 中的片段访问另一个片段?
Kotlin Android Studio - setContenView - 绑定(片段)
如何在 kotlin 中使 Material Button ToggleGroup 的所有子项不可点击