android 如何使用数据库作为文本状态的真实来源,同时使用MutableState快速更新TextInput?

41zrol4v  于 2023-05-12  发布在  Android
关注(0)|答案(1)|浏览(155)

在我的Jetpack Compose应用程序中,我使用Room来存储TODO项目列表,每个项目都有一个可编辑的文本。我希望将数据库作为唯一的事实来源(提供任务列表流),但为每个字符leads to issues异步更新DB。
我想使用一个MutableState来“缓存”文本输入和

  • 只要该高速缓存在一段时间内没有改变,就更新DB(可以通过将缓存MutableState转换为流并使用debounce来完成)
  • 当用户输入文本时该高速缓存,而且当DB更改时也更新缓存。如何做到这一点?

这就是我到现在为止所做的:

@Composable
fun TasksScreen(viewModel = ...) {
  val tasks: List<String> by viewModel.tasks.collectAsStateWithLifecycle()
  LazyColumn {
    items(items = tasks) { index, task ->
      Task(text = item, onTextChanged = { viewModel.setText(index, it) })
    }
  }
}

@Composable
fun Task(text: String, onTextChanged: (String) -> Unit) {
  val textState = remember { mutableStateOf("") }.apply { value = text }
  val textUpdateFlow = remember {
    snapshotFlow { textState.value }.debounce(1000).onEach { onTextChanged(it) }
  }
  BasicTextField(
    value = textState.value,
    onValueChange = { textState.value = it },
  )
}

问题:每当textCache被更新时,Task可组合项就会被重新组合,但这意味着原始文本会覆盖用户输入的文本,因此文本字段保持不变。另外,我不确定在组合中定义和记住textUpdateFlow是否正确,以及流的作用域。

zpqajqem

zpqajqem1#

这个问题的一个解决方案是使用不同的重组作用域,在与读取文本MutableState的作用域不同的作用域中创建文本MutableState。下面是一个通用代码片段,如果组合项需要一个应该更新的快速缓存状态,并且可以由一个较慢的外部状态(例如a DB)。

@Composable
fun <T> CachedUpdate(
  value: T,
  onValueChanged: (T) -> Unit,
  debounceMillis: Long = 1000,
  content: @Composable (MutableState<T>) -> Unit,
) {
  // this will run whenever a new value comes in from the outside (e.g. from DB)
  val cached = remember { mutableStateOf(value) }
  cached.value = value
  LaunchedEffect(onValueChanged, debounceMillis) {
    snapshotFlow { cached.value }.debounce(debounceMillis).onEach(onValueChanged).collect()
  }
  content(cached)
}

它可以这样使用:

@Composable
fun Task(text: String, onTextChanged: (String) -> Unit) {
  CachedUpdate(text, onTextChanged) {
    // this will re-run whenever the cached text is updated
    val (cachedText, setCachedText) = it
    BasicTextField(
      value = cachedText,
      onValueChange = setCachedText,
    )
  }
}

以下是它的工作原理:CachedUpdate可组合组件创建并记住缓存状态。在(重新)合成时,此状态将使用传入CachedUpdate的最新值进行更新。然后将缓存的状态对象传递给子组合对象,子组合对象可以读取和更新它。每当缓存的状态被更新时,只有子组合对象被重组,因为它有自己的重组作用域。这意味着我们避免了重新运行CachedUpdate组合,这将导致覆盖缓存状态。为了以去抖动的方式将缓存状态的更改写回数据库,我们将其转换为流并使用debounce方法。对于每次更新,我们调用提供的回调将数据写入DB。请注意,我们仍然需要收集流,因为snapshotFlowcold流。由于这是一个挂起函数,我们需要正确地确定它的范围,所以当我们的CachedUpdate可组合(以及包含的文本字段)离开组合时,它将被取消。这可以通过LaunchedEffect来实现。为了确保在流的任何参数(去抖时间和回调)发生变化时创建新的流,我们将这些参数作为key传递给LaunchedEffect

相关问题