如何实现输入时自动填充前置6个空白点的EditText组件
嘿,我刚好折腾过类似的验证码输入框需求!要实现这种带6个预设空白点、输入时自动填充的EditText,其实不用搞复杂的多控件联动,一个EditText配合TextWatcher和SpannableString就能轻松搞定。下面是具体的实现步骤和代码:
核心思路
用一个普通的EditText作为输入载体(隐藏原生输入框的视觉元素),通过监听输入内容的变化,动态生成对应数量的“填充点”和“空白点”,用Span来控制这些点的样式和间距,让用户看到的是一排点,实际输入逻辑还是由EditText处理,省心又稳定。
布局文件设置
首先在布局里添加EditText,把原生的背景、光标、文字都隐藏掉,只保留输入功能:
<EditText android:id="@+id/codeInputEt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@null" android:cursorVisible="false" android:inputType="number" <!-- 可根据需求改成textPassword或其他类型 --> android:maxLength="6" android:textColor="@android:color/transparent"/>
代码逻辑实现(Kotlin)
在Activity/Fragment里编写逻辑,初始化点的样式,监听输入变化并更新显示:
class CodeInputActivity : AppCompatActivity() { private lateinit var codeInputEt: EditText // 先用字符示例空白/填充点,后面会讲用自定义Drawable优化的方法 private val emptyDot = "○" private val filledDot = "●" private val totalDotCount = 6 // 预设的点数量 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_code_input) codeInputEt = findViewById(R.id.codeInputEt) // 初始显示6个空白点 updateDotDisplay("") // 添加输入监听 codeInputEt.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { s?.toString()?.let { updateDotDisplay(it) } } override fun afterTextChanged(s: Editable?) {} }) } private fun updateDotDisplay(inputContent: String) { val displayBuilder = StringBuilder() // 先添加已填充的点 repeat(inputContent.length) { displayBuilder.append(filledDot) } // 再添加剩余的空白点 repeat(totalDotCount - inputContent.length) { displayBuilder.append(emptyDot) } // 用SpannableString给点之间设置间距,避免挤在一起 val spannableText = SpannableString(displayBuilder.toString()) for (i in 0 until displayBuilder.length - 1) { spannableText.setSpan( MarginSpan(24), // 间距大小,单位px,可根据UI需求调整 i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) } // 更新EditText的显示 codeInputEt.setText(spannableText) // 保持光标在正确位置(虽光标隐藏,但输入时能正常接收内容) codeInputEt.setSelection(inputContent.length) } }
进阶优化:用自定义Drawable替换字符
如果觉得字符样式不够美观,完全可以用自定义Drawable来做空白/填充点,比如创建两个drawable文件:
dot_empty.xml(空心圆):
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <stroke android:width="2dp" android:color="#CCCCCC"/> <size android:width="40dp" android:height="40dp"/> </shape>
dot_filled.xml(实心圆):
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <solid android:color="#2196F3"/> <size android:width="40dp" android:height="40dp"/> </shape>
然后修改updateDotDisplay方法,用ImageSpan替换字符:
private fun updateDotDisplay(inputContent: String) { val spannableText = SpannableStringBuilder() // 添加已填充的点 repeat(inputContent.length) { spannableText.append(" ") // 占位符,后面替换成ImageSpan spannableText.setSpan( getDotSpan(isFilled = true), spannableText.length - 1, spannableText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) // 添加间距 spannableText.append(" ") } // 添加剩余的空白点 repeat(totalDotCount - inputContent.length) { spannableText.append(" ") spannableText.setSpan( getDotSpan(isFilled = false), spannableText.length - 1, spannableText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) spannableText.append(" ") } codeInputEt.setText(spannableText) codeInputEt.setSelection(inputContent.length * 2) // 每个点占两个字符(占位符+空格) } private fun getDotSpan(isFilled: Boolean): ImageSpan { val drawable = if (isFilled) { ContextCompat.getDrawable(this, R.drawable.dot_filled) } else { ContextCompat.getDrawable(this, R.drawable.dot_empty) } drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight) return ImageSpan(drawable!!, ImageSpan.ALIGN_BASELINE) }
额外小技巧
- 可以添加输入完成监听:当
inputContent.length == totalDotCount时,自动触发验证码验证或其他逻辑。 - 删除操作无需额外处理,TextWatcher会自动监听输入变化并更新点的显示。
- 给EditText添加点击事件,弹出系统软键盘,提升用户体验。
内容的提问来源于stack exchange,提问作者M.Salman Nazeer
相关产品推荐
相关产品推荐

