如何在Jetpack Compose中实现带IP验证与自动格式化的TextField?
在Jetpack Compose中实现带IP验证与自动格式化的TextField最简方案
你需要的IP地址+端口输入功能,完全可以通过单个TextField配合自定义视觉转换和输入逻辑实现,比XML拆分多个输入框的方案精简太多。下面是完整的可复用实现:
核心实现思路
- 用
VisualTransformation将纯数字输入转换为带.和:的IP+端口格式(仅视觉分段,实际输入值仍为纯数字,方便后续解析) - 在
onValueChange中处理输入验证:过滤非数字、限制每段IP不超过255、端口不超过65535 - 自动跳转逻辑:当前段输入满3位或数值超过25时自动切换到下一段;删除时自动回退到上一段
完整代码示例
import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material3.Text import androidx.compose.material3.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.ui.text.input.KeyboardType import androidx.compose.ui.text.input.VisualTransformation @Composable fun IPPortTextField( value: String, onValueChange: (String) -> Unit, label: String = "IP地址+端口" ) { TextField( value = value, onValueChange = { rawInput -> // 过滤所有非数字字符 val filtered = rawInput.filter { it.isDigit() } val processed = buildString { var segmentIndex = 0 var currentSegment = "" filtered.forEach { char -> currentSegment += char when (segmentIndex) { // 前4段为IP,每段最多3位,数值超25时自动跳转 0,1,2,3 -> { if (currentSegment.length == 3 || (currentSegment.length == 2 && currentSegment.toInt() > 25)) { append(currentSegment) if (segmentIndex < 3) append(".") currentSegment = "" segmentIndex++ } } // 第5段为端口,最多5位(符合0-65535范围) 4 -> { if (currentSegment.length == 5) { append(currentSegment) currentSegment = "" segmentIndex++ } } // 超出端口段的字符直接忽略 else -> return@forEach } } // 追加未完成的当前段 if (currentSegment.isNotEmpty()) append(currentSegment) } // 处理删除回退逻辑:删除到分隔符时自动回退上一段 val finalValue = if (rawInput.length < value.length && value.isNotEmpty()) { val lastChar = value.last() if (lastChar == '.' || lastChar == ':') processed.dropLast(1) else processed } else { processed } onValueChange(finalValue) }, label = { Text(label) }, keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number), visualTransformation = IPVisualTransformation() ) } // 自定义视觉转换器:将纯数字转换为IP+端口的格式化显示 class IPVisualTransformation : VisualTransformation { override fun filter(text: androidx.compose.ui.text.AnnotatedString): androidx.compose.ui.text.AnnotatedString { val rawText = text.text if (rawText.isEmpty()) return text return androidx.compose.ui.text.AnnotatedString.Builder().run { var segmentIndex = 0 var charCount = 0 rawText.forEach { char -> append(char) charCount++ when (segmentIndex) { 0,1,2 -> { if (charCount == 3) { append(".") charCount = 0 segmentIndex++ } } 3 -> { if (charCount == 3) { append(":") charCount = 0 segmentIndex++ } } // 端口段不添加分隔符 4 -> {} } } toAnnotatedString() } } } // 调用示例 @Composable fun IPPortInputDemo() { val ipPortValue = remember { mutableStateOf("") } IPPortTextField( value = ipPortValue.value, onValueChange = { ipPortValue.value = it } ) }
关键逻辑说明
输入过滤与分段:
- 自动过滤非数字字符,确保输入合法性
- IP段自动限制长度与数值范围,输入26时直接跳转下一段,避免输入256这类无效值
- 端口段限制最多5位,符合TCP/UDP端口的标准范围
视觉转换:
- 自动在IP段间添加
.,第4段IP后添加:分隔端口,视觉上和多输入框一致 - 实际绑定的
value仍为纯数字,后续解析IP和端口时无需额外处理格式
- 自动在IP段间添加
删除体验优化:
- 当删除操作触碰到分隔符时,自动回退到上一段的末尾,模拟多输入框的连贯删除体验
内容的提问来源于stack exchange,提问作者Sergio
相关产品推荐
相关产品推荐

