【问题标题】:How to convert a Flow<CustomType> to StateFlow<UIState>? - Android Kotlin如何将 Flow<CustomType> 转换为 StateFlow<UIState>? - 安卓科特林
【发布时间】:2022-07-22 10:09:33
【问题描述】:

我正在学习 Android 开发,正如我在许多主题中看到的那样,人们都在谈论不建议再使用 LiveData。我的意思是它不是最新的,我们应该改用 Flows。

我正在尝试使用 Flows 从 ROOM 数据库中获取数据,然后将它们转换为 StateFlow,因为我知道它们是可观察的,并且我还想向它们添加 UI 状态。就像我成功获取数据时,状态会更改为 Success,如果失败,则更改为 Error。

我有一个简单的练习应用程序。它存储订阅者的姓名和电子邮件,并在回收站视图中显示。

我查了很多网站,如何使用 stateIn 方法,如何使用 StateFlows 和 Flows 但没有成功。执行此操作的最佳方法是什么?

还有什么是更新 recyclerview 适配器的正确方法? MainActivity中一直改成新的adapter可以吗?

这是项目(SubscriberViewModel.kt - 第 30 行): Project link

如果我做错了其他事情,请告诉我,我想学习。感谢您的任何帮助。

道:

import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface SubscriberDAO {

@Insert
suspend fun insertSubscriber(subscriber : Subscriber) : Long

@Update
suspend fun updateSubscriber(subscriber: Subscriber) : Int

@Delete
suspend fun deleteSubscriber(subscriber: Subscriber) : Int

@Query("DELETE FROM subscriber_data_table")
suspend fun deleteAll() : Int

@Query("SELECT * FROM subscriber_data_table")
fun getAllSubscribers() : Flow<List<Subscriber>>

@Query("SELECT * FROM subscriber_data_table WHERE :id=subscriber_id")
fun getSubscriberById(id : Int) : Flow<Subscriber>

}

视图模型:

class SubscriberViewModel(private val repository: SubscriberRepository) : ViewModel() {

private var isUpdateOrDelete = false
private lateinit var subscriberToUpdateOrDelete: Subscriber

val inputName = MutableStateFlow("")
val inputEmail = MutableStateFlow("")

private val _isDataAvailable = MutableStateFlow(false)
val isDataAvailable : StateFlow<Boolean>
    get() = _isDataAvailable

val saveOrUpdateButtonText = MutableStateFlow("Save")
val deleteOrDeleteAllButtonText = MutableStateFlow("Delete all")

/*
//TODO - How to implement this as StateFlow<SubscriberListUiState> ??
//private val _subscribers : MutableStateFlow<SubscriberListUiState>
//val subscribers : StateFlow<SubscriberListUiState>
    get() = _subscribers
*/

private fun clearInput() {
    inputName.value = ""
    inputEmail.value = ""
    isUpdateOrDelete = false
    saveOrUpdateButtonText.value = "Save"
    deleteOrDeleteAllButtonText.value = "Delete all"
}

fun initUpdateAndDelete(subscriber: Subscriber) {
    inputName.value = subscriber.name
    inputEmail.value = subscriber.email
    isUpdateOrDelete = true
    subscriberToUpdateOrDelete = subscriber
    saveOrUpdateButtonText.value = "Update"
    deleteOrDeleteAllButtonText.value = "Delete"
}

fun saveOrUpdate() {
    if (isUpdateOrDelete) {
        subscriberToUpdateOrDelete.name = inputName.value
        subscriberToUpdateOrDelete.email = inputEmail.value
        update(subscriberToUpdateOrDelete)
    } else {
        val name = inputName.value
        val email = inputEmail.value
        if (name.isNotBlank() && email.isNotBlank()) {
            insert(Subscriber(0, name, email))
        }
        inputName.value = ""
        inputEmail.value = ""
    }
}

fun deleteOrDeleteAll() {
    if (isUpdateOrDelete) {
        delete(subscriberToUpdateOrDelete)
    } else {
        deleteAll()
    }
}

private fun insert(subscriber: Subscriber) = viewModelScope.launch(Dispatchers.IO) {
    repository.insert(subscriber)
    _isDataAvailable.value = true
}

private fun update(subscriber: Subscriber) = viewModelScope.launch(Dispatchers.IO) {
    repository.update(subscriber)
    clearInput()
}

private fun delete(subscriber: Subscriber) = viewModelScope.launch(Dispatchers.IO) {
    repository.delete(subscriber)
    clearInput()
}

private fun deleteAll() = viewModelScope.launch(Dispatchers.IO) {
    repository.deleteAll()
    //_subscribers.value = SubscriberListUiState.Success(emptyList())
    _isDataAvailable.value = false
}

sealed class SubscriberListUiState {
    data class Success(val list : List<Subscriber>) : SubscriberListUiState()
    data class Error(val msg : String) : SubscriberListUiState()
}
}

主活动:

class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: SubscriberViewModel
private lateinit var viewModelFactory: SubscriberViewModelFactory

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
    val dao = SubscriberDatabase.getInstance(application).subscriberDAO
    viewModelFactory = SubscriberViewModelFactory(SubscriberRepository(dao))
    viewModel = ViewModelProvider(this, viewModelFactory)[SubscriberViewModel::class.java]
    binding.viewModel = viewModel
    binding.lifecycleOwner = this
    initRecycleView()
}

private fun initRecycleView() {
    binding.recyclerViewSubscribers.layoutManager = LinearLayoutManager(
        this@MainActivity,
        LinearLayoutManager.VERTICAL, false
    )
    displaySubscribersList()
}

private fun displaySubscribersList() {
    /*
    lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            viewModel.subscribers.collect { uiState ->
                when (uiState) {
                    is SubscriberViewModel.SubscriberListUiState.Success -> {
                        binding.recyclerViewSubscribers.adapter = SubscriberRecyclerViewAdapter(uiState.list) {
                            subscriber: Subscriber -> listItemClicked(subscriber)
                        }
                    }
                    is SubscriberViewModel.SubscriberListUiState.Error -> {
                        Toast.makeText(applicationContext,uiState.msg, Toast.LENGTH_LONG).show()
                    }
                }
            }
        }
    }*/
}

private fun listItemClicked(subscriber: Subscriber) {
    Toast.makeText(this, "${subscriber.name} is selected", Toast.LENGTH_SHORT).show()
    viewModel.initUpdateAndDelete(subscriber)
}

}

【问题讨论】:

  • 请在您的问题中包含您在此处特别询问的相关代码。我认为您的问题不会得到太多答复,因为我们必须查看您的整个项目并尝试弄清楚您实际要问的是什么。

标签: android kotlin android-room kotlin-coroutines


【解决方案1】:

您可以使用stateIn 方法将Flow 类型转换为StateFlow。

 private val coroutineScope = CoroutineScope(dispatcher + Job())
 private val flow: Flow<CustomType>
 val stateFlow = flow.stateIn(scope = coroutineScope)

但是我不确定如何将 CustomType 转换为 UIState,也许 Flow 上的一些 transform 运算符会有所帮助,但需要进一步探索。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-23
    • 1970-01-01
    • 2019-12-17
    • 1970-01-01
    • 2023-02-14
    相关资源
    最近更新 更多