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

Android Jetpack Compose:实现银行卡号输入框每4位加空格并自动移焦

实现带自动空格的信用卡/借记卡卡号输入框

核心实现思路

要达成每输入4位数字自动添加空格、且光标自动跳转的效果,核心逻辑是监听文本变化→统一格式化文本→调整光标位置,同时限制仅允许输入数字。


iOS(Swift)实现方案

通过UITextFieldDelegate代理方法处理输入逻辑,确保格式和光标位置准确:

class CardNumberTextField: UITextField, UITextFieldDelegate {
    override func awakeFromNib() {
        super.awakeFromNib()
        self.delegate = self
        self.keyboardType = .numberPad // 弹出数字键盘
        self.maxLength = 19 // 16位卡号+3个空格的总长度
    }
    
    func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        // 仅允许输入数字或执行删除操作
        let allowedChars = CharacterSet.decimalDigits
        let inputChars = CharacterSet(charactersIn: string)
        guard allowedChars.isSuperset(of: inputChars) || string.isEmpty else {
            return false
        }
        
        guard let currentText = textField.text else { return true }
        let updatedText = (currentText as NSString).replacingCharacters(in: range, with: string)
        
        // 重新格式化文本:先移除所有空格,再每4位插入空格
        let strippedText = updatedText.replacingOccurrences(of: " ", with: "")
        let formattedText = strippedText.enumerated()
            .map { index, char in
                index > 0 && index % 4 == 0 ? " \(char)" : String(char)
            }
            .joined()
        
        // 更新文本,避免触发代理循环
        textField.text = formattedText
        
        // 计算并设置正确的光标位置
        let addedCharsCount = string.count
        let spaceAddedCount = formattedText.count - updatedText.count
        let newCursorPosition = range.location + addedCharsCount + spaceAddedCount
        
        if let newPosition = textField.position(from: textField.beginningOfDocument, offset: newCursorPosition) {
            textField.selectedTextRange = textField.textRange(from: newPosition, to: newPosition)
        }
        
        return false
    }
}

// 给UITextField添加maxLength扩展
extension UITextField {
    var maxLength: Int {
        get {
            guard let length = self.textField?.maximumLength else { return Int.max }
            return length
        }
        set {
            self.addTarget(self, action: #selector(limitLength), for: .editingChanged)
            self.textField?.maximumLength = newValue
        }
    }
    
    @objc private func limitLength() {
        guard let text = self.text, let maxLength = self.textField?.maximumLength else { return }
        if text.count > maxLength {
            self.text = text.prefix(maxLength).description
        }
    }
}

代码说明

  1. 输入限制:过滤非数字输入,避免干扰卡号格式
  2. 文本格式化:先清除所有空格再重新按规则插入,避免手动输入空格导致的格式混乱
  3. 光标调整:手动计算光标位置,确保插入空格后光标自动跳到空格后方,符合用户输入习惯

Android(Kotlin)实现方案

通过TextWatcher监听EditText的文本变化,自动格式化并调整光标:

class CardNumberTextWatcher(private val editText: EditText) : TextWatcher {
    private var isFormatting = false // 避免循环触发文本更新

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}

    override fun afterTextChanged(s: Editable?) {
        if (isFormatting) return
        isFormatting = true

        // 移除所有空格,重新格式化
        val originalText = s.toString().replace(" ", "")
        val formattedBuilder = StringBuilder()

        for (i in originalText.indices) {
            if (i > 0 && i % 4 == 0) {
                formattedBuilder.append(" ")
            }
            formattedBuilder.append(originalText[i])
        }

        // 更新文本并设置光标位置
        val formattedText = formattedBuilder.toString()
        editText.setText(formattedText)
        editText.setSelection(formattedText.length)

        isFormatting = false
    }
}

// 使用方式
val cardEditText = findViewById<EditText>(R.id.card_number_edittext)
cardEditText.addTextChangedListener(CardNumberTextWatcher(cardEditText))
cardEditText.inputType = InputType.TYPE_CLASS_NUMBER // 限制数字输入
cardEditText.filters = arrayOf(InputFilter.LengthFilter(19)) // 限制总长度

代码说明

  1. 防循环标记:isFormatting避免格式化文本后再次触发afterTextChanged
  2. 格式化逻辑:清除所有空格后重新按规则插入,保证格式统一
  3. 光标定位:每次格式化后将光标移到文本末尾,符合用户连续输入的习惯

注意事项

  • 限制总输入长度:信用卡卡号通常为16位,加上3个空格总长度为19,需设置对应长度限制
  • 兼容删除操作:上述代码已处理删除场景,删除空格或数字时会自动重新格式化文本
  • 适配不同卡号长度:部分借记卡可能为19位,可调整长度限制和格式化逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 04:22:57