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()
})