You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose文本框撤销重做系统开发问题求助

Jetpack Compose 文本框撤销/重做功能修复

问题1:定时器重置失效修复

  • 问题核心:之前的定时器未在用户输入时正确取消旧任务,导致旧延迟任务仍会触发记录逻辑。
  • 修复方式:使用协程Job管理延迟任务,每次输入时先取消当前的延迟Job,再启动新的延迟任务,确保只有用户停止输入达到设定时长后,才会触发历史记录。

问题2:合并短时间内输入记录

  • 问题核心:每输入一个字符就直接添加历史,未做批次合并。
  • 修复方式:仅在延迟任务触发时更新历史记录;同时,当用户在非最新历史位置输入时,清除后续的重做历史,保证撤销/重做逻辑的一致性。

修改后的核心代码(ViewModel层)

import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

class EditorViewModel : ViewModel() {
    var text by mutableStateOf("")
        private set

    private val history = mutableListOf("")
    var currentIndex by mutableIntStateOf(0)
        private set

    private var delayJob: Job? = null
    // 可根据需求调整延迟时长,比如1000ms即停止输入1秒后记录
    private val DEBOUNCE_DELAY = 1000L

    fun onTextChanged(newText: String) {
        text = newText
        // 重置定时器:取消旧任务,启动新延迟
        delayJob?.cancel()
        delayJob = CoroutineScope(Dispatchers.Main).launch {
            delay(DEBOUNCE_DELAY)
            updateHistory()
        }
    }

    private fun updateHistory() {
        if (text == history[currentIndex]) return

        // 如果当前不是最新历史,清除后续重做记录
        if (currentIndex < history.size - 1) {
            history.subList(currentIndex + 1, history.size).clear()
        }

        history.add(text)
        currentIndex = history.size - 1
    }

    fun undo() {
        if (currentIndex > 0) {
            currentIndex--
            text = history[currentIndex]
            delayJob?.cancel()
        }
    }

    fun redo() {
        if (currentIndex < history.size - 1) {
            currentIndex++
            text = history[currentIndex]
            delayJob?.cancel()
        }
    }
}

Compose界面调用示例

@Composable
fun EditorScreen(viewModel: EditorViewModel = viewModel()) {
    Column(modifier = Modifier.padding(16.dp)) {
        TextField(
            value = viewModel.text,
            onValueChange = viewModel::onTextChanged,
            modifier = Modifier.fillMaxWidth().weight(1f),
            placeholder = { Text("输入内容...") }
        )
        Row(
            modifier = Modifier.fillMaxWidth().padding(top = 8.dp),
            horizontalArrangement = Arrangement.Center,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Button(
                onClick = viewModel::undo,
                enabled = viewModel.currentIndex > 0,
                modifier = Modifier.padding(end = 16.dp)
            ) {
                Text("撤销")
            }
            Button(
                onClick = viewModel::redo,
                enabled = viewModel.currentIndex < viewModel.history.size - 1
            ) {
                Text("重做")
            }
        }
    }
}

关键逻辑说明

  1. 定时器重置:每次输入时通过delayJob?.cancel()终止旧的延迟任务,再启动新任务,确保只有停止输入后才会记录历史。
  2. 批次合并:延迟触发时才将当前文本加入历史,短时间内连续输入会被合并为一条记录。
  3. 历史一致性:当用户在已撤销的历史位置输入新内容时,清除后续的重做记录,避免出现逻辑混乱。

内容的提问来源于stack exchange,提问作者Bob Rasner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 07:05:18