First, the final effect drawing
Analysis of implementation ideas: 1. Use a LinearLayout to fill each of the small squares, and dynamically add the number of input boxes needed. 2. Set the EditText input background and text to transparent, and set no display cursor. 3. Listen to the EditText content change, and bind the Content to the LinearLayout, so that each input is displayed by the sublayout of the LinearLayout
Layout file
<? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content"> <LinearLayout android:orientation="horizontal" android:id="@+id/rvContentList" android:gravity="center" android:showDividers="middle" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <EditText android:id="@+id/inputReal" android:inputType="number" android:background="@android:color/transparent" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/transparent"/> </RelativeLayout>Copy the code
Dynamically create the LinearLayout sublayout fill in your code and bind the listener
Private Fun initContainer() {// Dynamically set the size of the EditText inputReal = findViewById(R.id.inputreal) rvContentList = findViewById(R.id.rvContentList) inputReal.width = (dividerDrawable? .minimumWidth ? : 0 * (verifyCodeLen - 1)) + inputBoxSize * verifyCodeLen inputReal.height = inputBoxSize inputReal.setTextSize(TypedValue.COMPLEX_UNIT_PX, InputTextSize * 1.0 F) / / disable the cursor inputReal isCursorVisible = false inputReal. Filters = ArrayOf (inputfilter.lengthfilter (verifyCodeLen)) inputtextview.clear () // dynamically add dividerDrawable between LinearLayout? .let { it.setBounds(0, 0, it.minimumWidth, it.minimumHeight) rvContentList.dividerDrawable = it } for (i in 0 until verifyCodeLen) { val textView = TextView(context) textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, Width = inputBoxSize TextView. height = inputBoxSize textView.gravity = gravity textView.isFocusable = false textView.textColor = inputTextColor textView.backgroundResource = itemSelector inputTextView.add(textView) } inputTextView.forEach { rvContentList.addView(it) } }Copy the code
inputReal.addTextChangedListener(object : TextWatcher { override fun afterTextChanged(p0: Editable?) { setVerifyCodeInputValue(p0.toString()) if (p0.toString().length == verifyCodeLen) { onCompleteListener? .onComplete(p0.toString()) } } override fun beforeTextChanged(p0: CharSequence? , p1: Int, p2: Int, p3: Int) { } override fun onTextChanged(p0: CharSequence? , p1: Int, p2: Int, p3: Int) { } })Copy the code
private fun setVerifyCodeInputValue(inputText: String) {
inputTextView.forEach {
it.text = ""
it.isSelected = false
}
inputTextView.forEachIndexed { index, textView ->
if (inputText.length > index) {
textView.isSelected = true
textView.text = inputText[index].toString()
}
}
}
Copy the code
The core code is here, in order to facilitate the extension, you can add custom attributes, dynamically set the extension effect, here is not clear, directly look at the code
Finally put the full source code:
package org.fireking.ap.custom.viewgroup.view
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.text.Editable
import android.text.InputFilter
import android.text.Spanned
import android.text.TextWatcher
import android.util.AttributeSet
import android.util.Log
import android.util.TypedValue
import android.view.Gravity
import android.view.LayoutInflater
import android.widget.*
import androidx.core.view.forEach
import androidx.recyclerview.widget.RecyclerView
import org.fireking.ap.R
import org.jetbrains.anko.backgroundColor
import org.jetbrains.anko.backgroundResource
import org.jetbrains.anko.textColor
class VerifyCodeInputLayout(context: Context, attrs: AttributeSet?, defStyleAttr: Int) :
RelativeLayout(context, attrs, defStyleAttr) {
private lateinit var inputReal: EditText
private lateinit var rvContentList: LinearLayout
private var onCompleteListener: OnCompleteListener? = null
private var verifyCodeLen = 0
private var inputTextSize: Int = 0
private var inputTextColor: Int = 0
private var inputBoxSize: Int = 0
private var verifyInputLayoutHeight = 0
private var dividerDrawable: Drawable? = null
private var itemSelector: Int = R.drawable.verify_code_text_selector
private var inputTextView = ArrayList<TextView>(4)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
LayoutInflater.from(context).inflate(R.layout.verify_code_input_layout, this, true)
//设置默认值
verifyCodeLen = 4
inputTextSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16.0F, resources.displayMetrics).toInt()
inputTextColor = Color.parseColor("#FF333333")
inputBoxSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 50F, resources.displayMetrics).toInt()
dividerDrawable = context.resources.getDrawable(R.drawable.linearlayout_divider)
//获取自定义属性值
val a = context.obtainStyledAttributes(attrs, R.styleable.VerifyCodeInputLayout)
if (a.hasValue(R.styleable.VerifyCodeInputLayout_textSize)) {
inputTextSize = a.getDimensionPixelSize(R.styleable.VerifyCodeInputLayout_textSize, inputTextSize)
}
if (a.hasValue(R.styleable.VerifyCodeInputLayout_textColor)) {
inputTextColor = a.getColor(R.styleable.VerifyCodeInputLayout_textColor, Color.parseColor("#FF333333"))
}
if (a.hasValue(R.styleable.VerifyCodeInputLayout_inputBoxSize)) {
inputBoxSize = a.getDimensionPixelSize(
R.styleable.VerifyCodeInputLayout_inputBoxSize,
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 44F, resources.displayMetrics).toInt()
)
}
if (a.hasValue(R.styleable.VerifyCodeInputLayout_dividerDrawable)) {
dividerDrawable = a.getDrawable(R.styleable.VerifyCodeInputLayout_dividerDrawable)
}
if (a.hasValue(R.styleable.VerifyCodeInputLayout_itemSelector)) {
itemSelector = a.getResourceId(R.styleable.VerifyCodeInputLayout_itemSelector, itemSelector)
}
if (a.hasValue(R.styleable.VerifyCodeInputLayout_maxLength)) {
verifyCodeLen = a.getInt(R.styleable.VerifyCodeInputLayout_maxLength, 4)
}
a.recycle()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
verifyInputLayoutHeight = measuredHeight
}
fun setOnCompleteListener(onCompleteListener: OnCompleteListener) {
this.onCompleteListener = onCompleteListener
}
override fun onFinishInflate() {
super.onFinishInflate()
initContainer()
initListener()
}
private fun initListener() {
inputReal.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(p0: Editable?) {
setVerifyCodeInputValue(p0.toString())
if (p0.toString().length == verifyCodeLen) {
onCompleteListener?.onComplete(p0.toString())
}
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
})
}
private fun setVerifyCodeInputValue(inputText: String) {
inputTextView.forEach {
it.text = ""
it.isSelected = false
}
inputTextView.forEachIndexed { index, textView ->
if (inputText.length > index) {
textView.isSelected = true
textView.text = inputText[index].toString()
}
}
}
private fun initContainer() {
inputReal = findViewById(R.id.inputReal)
rvContentList = findViewById(R.id.rvContentList)
inputReal.width = (dividerDrawable?.minimumWidth ?: 0 * (verifyCodeLen - 1)) + inputBoxSize * verifyCodeLen
inputReal.height = inputBoxSize
inputReal.setTextSize(TypedValue.COMPLEX_UNIT_PX, inputTextSize * 1.0F)
inputReal.isCursorVisible = false
inputReal.filters = arrayOf(InputFilter.LengthFilter(verifyCodeLen))
inputTextView.clear()
dividerDrawable?.let {
it.setBounds(0, 0, it.minimumWidth, it.minimumHeight)
rvContentList.dividerDrawable = it
}
for (i in 0 until verifyCodeLen) {
val textView = TextView(context)
textView.setTextSize(TypedValue.COMPLEX_UNIT_PX, inputTextSize * 1.0F)
textView.width = inputBoxSize
textView.height = inputBoxSize
textView.gravity = Gravity.CENTER
textView.isFocusable = false
textView.textColor = inputTextColor
textView.backgroundResource = itemSelector
inputTextView.add(textView)
}
inputTextView.forEach {
rvContentList.addView(it)
}
}
interface OnCompleteListener {
fun onComplete(content: String)
}
}
Copy the code
Custom attributes
<? The XML version = "1.0" encoding = "utf-8"? > <resources> <declare-styleable name="VerifyCodeInputLayout"> <attr name="textSize" format="dimension"/> <attr name="textColor" format="color"/> <attr name="inputBoxSize" format="dimension"/> <attr name="dividerDrawable" format="reference"/> <attr name="maxLength" format="integer"/> <attr name="itemSelector" format="reference"/> </declare-styleable> </resources>Copy the code