将 android 中的数据库持久状态和仅 UI 状态与 kotlin 相结合的好方法是什么?

问题描述 投票:0回答:1

我正在使用 Jetpack compose 和 Room 作为数据库解决方案为 android 编写一个 Todo 应用程序。 对于每个待办事项/任务,我都有一些数据需要保存在数据库中(例如文本、截止日期……)和一些仅用于 UI 的数据(例如,如果任务当前已展开,则显示更多详细信息)。

数据库/存储库为我提供了一系列

Task
列表:

@Entity(tableName="task")
data class Task(
  @PrimaryKey(autoGenerate = true) val id: Int = 0,
  val text: String,
  val dueDate: LocalDate,
)

@Dao
interface TaskDao {
  // ...other methods
  @Query("SELECT * FROM task") fun getAllTasks(): Flow<List<Task>>
}

class TaskRepository(private val taskDao: TaskDao) {
  // ...other methods
  override fun getAllTasks() = taskDao.getAllTasks()
}

为了在 UI 中显示任务,我有一个

TaskUiState
类。我的屏幕级 ViewModel 当前公开了
TaskUiState
列表流:

data class TaskUiState(
  val id: Int,
  val text: String,
  val dueDate: LocalDate,
  val expanded: Boolean = false,
)

class MainScreenViewModel(
  private val taskRepository: TaskRepository,
) : ViewModel() {

  val taskList: StateFlow<List<TaskUiState>> =
    taskRepository
      .getAllTasks()
      .map { tasks -> tasks.map { TaskUiState(it.id, it.text, it.dueDate) } }
      .stateIn(
        scope = viewModelScope,
        started = SharingStarted.WhileSubscribed(5_000),
        initialValue = emptyList()
      )
}

我现在的问题是,如果任务扩展或不扩展,我无法更改,因为我不知道如何为每个任务正确存储此信息并将其映射到数据库实体。

我认为这是一个常见问题,但找不到任何相关信息。执行此操作的良好且性能合理的方法是什么? 我考虑过将

Map<Int, Boolean>
从任务 ID 存储到 isExpanded。然后在将
Task
s 映射到
TaskUiState
s 时使用此查找。 我需要确保
taskList
流在此地图更改时更新,因此我可能需要使用
StateFlow<Map<Int, Boolean>>
并使用流
combine
功能。这种方法可行吗?请注意,我不是在为“扩展”状态寻找特定的解决方案(因为这只是一个最小的例子),而是在寻找一种更通用的方法来组合 DB 持久状态和 UI 状态。

android kotlin android-room android-viewmodel kotlin-flow
1个回答
0
投票

理想情况下使用

combine
是更好的方法,但只需使用
Set
而不是
Map


class MainScreenViewModel(
  private val taskRepository: TaskRepository,
) : ViewModel() {

    private val expandedTaskFlow: MutableStateFlow<MutableSet<Int>> = MutableStateFlow(mutableSetOf())
    
    val taskList: StateFlow<List<TaskUiState>> = 
            combine(taskRepository.getAllTasks(), expandedTaskFlow, ::taskUIState)
                    .stateIn(
                            scope = viewModelScope,
                            started = SharingStarted.WhileSubscribed(5_000),
                            initialValue = emptyList()
                    )
    
    private fun taskUIState(tasks: List<Task>, expandedSet: MutableSet<Int>): List<TaskUiState> {
        return  tasks.map { TaskUiState(it.id, it.text, it.dueDate, it.id in expandedSet) } 
    }
    
    fun expand(taskId: Int) {
        expandedTaskFlow.value = expandedTaskFlow.value.apply { add(taskId) }
    }

    fun collpase(taskId: Int) {
        expandedTaskFlow.value = expandedTaskFlow.value.apply { remove(taskId) }
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.