android验证码输入框列表的一种实现

android上经常会有验证码的输入要求。
网上的实现也是五花八门,有的直接使用EditText,有的使用隐藏的Edit+前台可见的多个TextView,大部分界面已经由他们定死。

我这里提供一种思路。

界面随便你的怎么写。
只需要使用我的manager类即可。

第一,将Edit,继承一个TextInputEditText类,增加额外的限制方法:

	
    private var onSelectionChanged:((edit:FontEdit, selStart:Int, selEnd:Int)->Unit)? = null

    fun doOnSelectionChanged(change:(edit:FontEdit, selStart:Int, selEnd:Int)->Unit) {
        onSelectionChanged = change
    }

    override fun onSelectionChanged(selStart: Int, selEnd: Int) {
        super.onSelectionChanged(selStart, selEnd)
        onSelectionChanged?.invoke(this, selStart, selEnd)
    }

	/**
	 * 设置最大长度
	 */
	fun EditText.setMaxLength(max: Int) {
	    addFilters(InputFilter.LengthFilter(max))
	}

	fun EditText?.addFilters(vararg filter: InputFilter) {
	    this ?: return
	    if (filter.isNullOrEmpty()) {
	        return
	    }
	    val old = this.filters
	    val new = arrayOf<InputFilter>(*old, *filter)
	    this.filters = new
	}

第二步,你随意写布局,往这里传入数组形式即可。并且,代码中去判断是否是


import android.text.Editable
import android.text.TextWatcher
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import com.google.android.material.textfield.TextInputEditText
import java.lang.StringBuilder

/**
 * @author allan
 * Date: 2023/4/18
 * Description 验证码的帮助类
 * 自行随便写布局。只需要按照顺序传入列表即可。
 *
 */
class CodePairListManager(private val editList:Array<TextInputEditText>) {
    /**
     * 用于监听跳转。
     */
    var allEnterCodeListener:((String)->Unit)? = null

    private val maxTagIndex:Int = editList.size - 1
    private val editTextChangeList = ArrayList<TextWatcherWithHost>()

    init {
        val onFocusChange = View.OnFocusChangeListener { v, hasFocus ->
            //logd("on focus change tag: ${v.tag} focus: $hasFocus")
            val edit = v as TextInputEditText
            val textLen = edit.text?.length ?: 0
            if (hasFocus) {
                edit.isCursorVisible = true
                if (textLen > 0) {
                    edit.setSelection(textLen)
                }
            } else {
                edit.isCursorVisible = false
            }
        }

        val onKeyListener = View.OnKeyListener { v, keyCode, event ->
            val edit = v as TextInputEditText
            var r = false
            if (event.action == KeyEvent.ACTION_DOWN) {
                when (keyCode) {
                    KeyEvent.KEYCODE_DEL -> {
                        val curIndex = edit.tag as Int

                        setTextWithoutNotify(curIndex, "")
                        if (curIndex != 0) {
                            val prev = prevFocusEdit(curIndex)
                            prev?.requestFocus()
                        }
                        r = true
                    }
                    KeyEvent.KEYCODE_ENTER -> {
                        if (isAllHasText()) {
                            allEnterCodeListener?.invoke(allCombineString())
                        }
                        r = true
                    }
                }
            }

            r
        }

        editList.forEachIndexed { index, edit ->
            edit.tag = index
            edit.isCursorVisible = false
            edit.setMaxLength(2)
            edit.inputType = EditorInfo.TYPE_CLASS_NUMBER
            edit.onFocusChangeListener = onFocusChange
            val watcher = TextWatcherWithHost(this, index)
            editTextChangeList.add(watcher)
            edit.addTextChangedListener(watcher)
            edit.setOnKeyListener(onKeyListener)
            edit.asOrNull<FontEdit>()?.let {
                it.doOnSelectionChanged { edit, selStart, selEnd ->
                    //logd("sel start $selStart $selEnd")
                    if (selStart == 0) {
                        edit.setSelection(edit.text?.length ?: 0)
                    }
                }
            }
        }
    }


    private fun nextFocusEdit(curIndex:Int) : TextInputEditText? {
        return when (curIndex) {
            maxTagIndex -> null
            else -> editList[curIndex + 1]
        }
    }

    private fun prevFocusEdit(curIndex:Int) : TextInputEditText? {
        return when (curIndex) {
            0 -> null
            else -> editList[curIndex - 1]
        }
    }

    private fun setTextWithoutNotify(editIndex:Int, text:String) {
        val edit = editList[editIndex]
        edit.removeTextChangedListener(editTextChangeList[editIndex])
        edit.setText(text)
        edit.addTextChangedListener(editTextChangeList[editIndex])
    }

    private fun setTextWithoutNotify(edit:TextInputEditText, text:String) {
        val editIndex = edit.tag as Int
        edit.removeTextChangedListener(editTextChangeList[editIndex])
        edit.setText(text)
        edit.addTextChangedListener(editTextChangeList[editIndex])
    }

    private fun isAllHasText():Boolean {
        editList.forEach {
            val len = it.text?.length ?: 0
            if (len == 0) {
                return false
            }
        }

        return true
    }

    private fun allCombineString():String {
        val sb = StringBuilder()
        editList.forEach {
            sb.append(it.text)
        }

        return sb.toString()
    }

    private class TextWatcherWithHost(private val mgr:CodePairListManager, private val index:Int) : TextWatcher {
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            //logd("index${index}: beforeTextChanged $s : $start $count $after")
        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            //logd("index${index}: onTextChanged $s : $start $before $count")
            if (s?.length == 2) {
                mgr.setTextWithoutNotify(index, s.substring(start, start + 1))
            }
        }

        override fun afterTextChanged(s: Editable?) {
            //logd("index${index}: after text changed")
            val next = mgr.nextFocusEdit(index)
            next?.requestFocus()

            if (mgr.isAllHasText()) {
                mgr.allEnterCodeListener?.invoke(mgr.allCombineString())
            }
        }
    }
}

几个优点:

    1. 随便自行写布局,按照数组传入;
    1. 自动跳到下一个;光标始终在末尾。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值