Android短信验证码输入框
放开那只肥鸡 人气:0其实用官方自定的那个inputEditText默认带下划线的,然后自己再实行焦点和输入框弹出等操作也可以。
写这个自定义View主要是为了练习。
/** * 实现了粘贴事件监听回调的 EditText */ open class ListenPasteEditTextTest : AppCompatEditText { constructor(context: Context): super(context) constructor(context: Context, attributeSet: AttributeSet): super(context,attributeSet) constructor(context: Context, attributeSet: AttributeSet, defStyleAttr: Int): super(context, attributeSet, defStyleAttr) var lisenter: ClipInterface ? = null override fun onTextContextMenuItem(id: Int): Boolean { when(id) { //剪切复制黏贴 android.R.id.cut -> lisenter?.onCut(); android.R.id.copy -> lisenter?.onCopy(); android.R.id.paste -> lisenter?.onPaste(); } return super.onTextContextMenuItem(id) } } interface ClipInterface{ fun onCut() fun onCopy() fun onPaste() }
/** * 手机验证码输入控件 */ class VerificationCodeInputTest(context: Context, attributeSet: AttributeSet) : ViewGroup(context, attributeSet), ClipInterface{ private val box = 4 private val boxWidth = 120 private val boxHeight = 120 private var childPadding = 14 private val TYPE_NUMBER = "number" private val TYPE_TEXT = "text" private val TYPE_PASSWORD = "password" private val TYPE_PHONE = "phone" private val boxBgFocus: Drawable? = null private val boxBgNormal: Drawable? = null private val inputType = TYPE_NUMBER var listener: VerCideListener? = null init { val textWatcher = object : TextWatcher{ 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 (s != null) { if (s.isNotEmpty()) { focus() checkAndCommit() } } } } val onKeyListener = OnKeyListener { v, keyCode, event -> if (keyCode == KeyEvent.KEYCODE_DEL) { //backFocus(); backFocusClearAll() } false } //四个输入框 for (index in 0 until box) { val editText = ListenPasteEditTextTest(context) val layoutParams = LinearLayout.LayoutParams(boxWidth, boxHeight) layoutParams.bottomMargin = childPadding layoutParams.topMargin = childPadding layoutParams.leftMargin = childPadding layoutParams.rightMargin = childPadding layoutParams.gravity = Gravity.CENTER editText.layoutParams = layoutParams editText.lisenter = this editText.setOnKeyListener(onKeyListener) //设置背景颜色,就是输入框中的下划线 setBg(editText, false) editText.setTextColor(Color.BLACK) editText.gravity = Gravity.CENTER //最多给你输入一个字符 editText.filters = arrayOf<InputFilter>(InputFilter.LengthFilter(1)) //设置textView输入内容的显示模式 if (TYPE_PASSWORD == inputType) { editText.transformationMethod = PasswordTransformationMethod.getInstance() } else if (TYPE_TEXT == inputType) { editText.inputType = InputType.TYPE_CLASS_TEXT } else if (TYPE_PHONE == inputType) { editText.inputType = InputType.TYPE_CLASS_PHONE } editText.id = index //设置字符宽度 editText.setEms(1) editText.addTextChangedListener(textWatcher) addView(editText, index) } } override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { var parentWidth = measuredWidth //如果在xml中配置的是match_patent 则直接获取当前手机的width尺寸 if (parentWidth == ViewGroup.LayoutParams.MATCH_PARENT) { parentWidth = getScreenWidth() } Log.d(javaClass.name, "onMeasure width $parentWidth") val count = childCount for (i in 0 until count) { val child = getChildAt(i) this.measureChild(child, widthMeasureSpec, heightMeasureSpec) } if (count > 0) { val child = getChildAt(0) val cWidth = child.measuredWidth if (parentWidth != ViewGroup.LayoutParams.WRAP_CONTENT) { // 重新计算padding childPadding = (parentWidth - cWidth * count) / (count + 1) } val cHeight = child.measuredHeight val maxH = cHeight + 2 * childPadding val maxW = cWidth * count + childPadding * (count + 1) //上面都是计算当前editText的width加上pandding,之后设置给父布局 setMeasuredDimension( View.resolveSize(maxW, widthMeasureSpec), View.resolveSize(maxH, heightMeasureSpec) ) } } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { val childCount = childCount for (i in 0 until childCount) { val child = getChildAt(i) child.visibility = View.VISIBLE val cWidth = child.measuredWidth val cHeight = child.measuredHeight val cl = childPadding + i * (cWidth + childPadding) val cr = cl + cWidth val ct = childPadding val cb = ct + cHeight child.layout(cl, ct, cr, cb) } } private fun getScreenWidth(): Int { val resources = this.resources val dm = resources.displayMetrics return dm.widthPixels } override fun onCut() { } override fun onCopy() { } override fun onPaste() { val copyText = getCutAndCopyText() // 如果是数字并且 length 和 填写位数一致才会进行填充 if (isNumeric(copyText) && copyText.length == box) { for (i in 0 until childCount) { (getChildAt(i) as EditText).append(copyText.get(i).toString()) } } } fun setBg(editText: EditText, focus: Boolean) { if (boxBgNormal != null && !focus) { editText.background = boxBgNormal } else if (boxBgFocus != null && focus) { editText.background = boxBgFocus } } private fun focus() { val count = childCount var editText: EditText for (i in 0 until count) { editText = getChildAt(i) as EditText if (editText.text.isEmpty()) { editText.requestFocus() return } } } private fun checkAndCommit() { val stringBuilder = StringBuilder() var full = false for (i in 0 until box) { val editText = getChildAt(i) as EditText val content = editText.text.toString() if (!content.isEmpty()) { stringBuilder.append(content) } } if (stringBuilder.length == box) { full = true } if (full) { if (listener != null) { listener?.onComplete(stringBuilder.toString()) backFocusClearAll() } } } //清空所有并重新输入 fun backFocusClearAll() { var editText: EditText for (i in 0 until box) { editText = getChildAt(i) as EditText editText.setText("") editText.clearFocus() } getChildAt(0).requestFocus() } /** * 判断是否是数字 * * @param str * @return */ private fun isNumeric(str: String?): Boolean { if (str == null || str.isEmpty()) { return false } for (i in 0 until str.length) { if (!Character.isDigit(str[i])) { return false } } return true } /** * 获取剪贴板内容 */ private fun getCutAndCopyText(): String { val manager = context.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager if (manager != null && manager.hasPrimaryClip() && manager.primaryClip!!.itemCount > 0) { val addedText = manager.primaryClip!!.getItemAt(0).text if (addedText != null) { return addedText.toString() } } return "" } } interface VerCideListener { fun onComplete(content: String) }
加载全部内容