Android如何实现带分段提示的16位借记卡号码EditText?
实现借记卡号码输入EditText的方案
核心思路
通过自定义EditText结合TextWatcher监听输入内容,同时利用SpannableString处理提示文本(Hint)的分段格式,并给横线部分设置不可选中标记,确保用户只能操作输入的数字内容。
具体实现步骤
1. 自定义EditText类
创建DebitCardEditText继承自AppCompatEditText,重写光标和选择相关方法,限制选中范围仅为用户输入的数字区域:
class DebitCardEditText @JvmOverloads constructor( context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = R.attr.editTextStyle ) : AppCompatEditText(context, attrs, defStyleAttr) { private val separator = " " private val cardLength = 16 private val segmentLength = 4 private val totalSegments = cardLength / segmentLength init { initHint() inputType = InputType.TYPE_CLASS_NUMBER addTextChangedListener(CardTextWatcher()) } private fun initHint() { val hintBuilder = StringBuilder() repeat(totalSegments) { index -> hintBuilder.append("----") if (index != totalSegments - 1) { hintBuilder.append(separator) } } // 给横线设置不可选中的Span val spannableHint = SpannableString(hintBuilder.toString()) spannableHint.setSpan( object : Selection() { override fun canSelectArbitrarily(): Boolean = false }, 0, spannableHint.length, Spannable.SPAN_INCLUSIVE_INCLUSIVE ) hint = spannableHint } // 限制选中范围仅为输入的数字区域 override fun onSelectionChanged(selStart: Int, selEnd: Int) { val filteredText = text?.toString()?.replace(separator, "") ?: "" if (filteredText.isEmpty()) { setSelection(0) return } // 计算实际可选中的范围,跳过分隔符 var actualStart = selStart var actualEnd = selEnd var spaceCount = 0 for (i in 0 until selStart) { if (text?.get(i) == ' ') spaceCount++ } actualStart -= spaceCount spaceCount = 0 for (i in 0 until selEnd) { if (text?.get(i) == ' ') spaceCount++ } actualEnd -= spaceCount // 确保选中范围不超出已输入内容 val maxSelect = filteredText.length + (filteredText.length / segmentLength) if (actualStart > maxSelect) actualStart = maxSelect if (actualEnd > maxSelect) actualEnd = maxSelect super.onSelectionChanged(actualStart, actualEnd) } inner class CardTextWatcher : TextWatcher { private var isUpdating = false override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { if (isUpdating) return isUpdating = true // 过滤非数字字符 val cleanText = s?.toString()?.replace(Regex("[^0-9]"), "") ?: "" val builder = StringBuilder() // 自动添加分隔符 for (i in cleanText.indices) { if (i != 0 && i % segmentLength == 0) { builder.append(separator) } builder.append(cleanText[i]) } setText(builder.toString()) setSelection(builder.length) isUpdating = false } override fun afterTextChanged(s: Editable?) {} } }
2. 布局中引用自定义控件
在XML布局文件中直接使用该控件:
<com.yourpackage.DebitCardEditText android:layout_width="match_parent" android:layout_height="wrap_content" android:maxLength="19" <!-- 16位数字 + 3个空格分隔符 --> android:gravity="center" />
3. 关键细节说明
- 提示文本不可选中:通过
SpannableString给横线部分设置自定义SelectionSpan,重写canSelectArbitrarily返回false,阻止横线被选中。 - 自动分段格式:
TextWatcher过滤非数字内容后,每输入4位自动添加空格分隔符,保持格式规范。 - 光标与选中控制:重写
onSelectionChanged方法,计算实际可操作的数字区域,跳过分隔符和未输入的横线部分,确保用户仅能操作自己输入的数字。 - 输入长度限制:设置
maxLength为19,避免超出16位数字的输入限制。
内容的提问来源于stack exchange,提问作者james04
相关产品推荐
相关产品推荐

