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

TextField视觉转换报错:OffsetMapping.originalToTransformed返回无效映射

解决方案

错误原因

你遇到的offsetmapping.originaltotransformed returned invalid mapping错误,是因为原掩码逻辑在文本长度小于4时,生成的转换后文本长度(固定4个*)与原始文本长度不一致,而OffsetMapping.Identity要求原始和转换后的文本长度完全匹配,导致光标位置映射失效。

核心修复步骤

  1. 保证转换前后文本长度一致:掩码长度随原始文本动态调整,文本长度≤4时掩码全部字符,长度>4时仅掩码前4个字符,避免固定生成4个*。
  2. 区分编辑/非编辑状态:通过状态变量控制掩码开关,用户开始编辑时自动关闭掩码。

完整代码实现

import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.TextField
import androidx.compose.runtime.*
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.input.VisualTransformation
import androidx.compose.ui.text.input.OffsetMapping

// 自定义用户名掩码转换
class UsernameMaskTransformation : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        return maskFilter(text)
    }
}

private fun maskFilter(text: AnnotatedString): TransformedText {
    // 动态计算掩码长度:最多4个,文本长度不足4时掩码全部字符
    val maskCount = min(4, text.length)
    val maskedContent = "*".repeat(maskCount) + text.substring(maskCount)
    // 转换前后长度一致,直接用Identity映射即可
    return TransformedText(AnnotatedString(maskedContent), OffsetMapping.Identity)
}

// 登录表单示例
@Composable
fun LoginUsernameField() {
    // 用户名状态
    var username by remember { mutableStateOf("") }
    // 编辑状态标记:true时关闭掩码
    var isEditing by remember { mutableStateOf(false) }
    // 保存原始已存储的用户名(模拟从本地加载)
    val originalUsername by remember { mutableStateOf("johndoe") }

    // 初始化加载已保存的用户名
    LaunchedEffect(Unit) {
        username = originalUsername
        isEditing = false
    }

    TextField(
        value = username,
        onValueChange = { input ->
            // 用户开始输入时切换到编辑状态,关闭掩码
            if (!isEditing) {
                isEditing = true
            }
            username = input
        },
        visualTransformation = if (isEditing) VisualTransformation.None else UsernameMaskTransformation(),
        label = { androidx.compose.material3.Text("用户名") },
        onFocusChanged = { focusState ->
            // 失去焦点且未修改用户名时,恢复掩码显示
            if (!focusState.isFocused && username == originalUsername) {
                isEditing = false
            }
        }
    )
}

逻辑说明

  • 非编辑状态:显示已保存的用户名时,自动掩码前4个字符(文本长度≤4时全掩码),转换前后文本长度一致,光标映射不会出错。
  • 编辑状态:用户首次输入字符时自动关闭掩码;若用户未修改用户名并失去焦点,会重新恢复掩码显示;若修改了用户名,则保持掩码关闭状态。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 18:53:18