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

如何实现输入时自动填充前置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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 18:57:45