验证码输入框

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.text.InputFilter
import android.text.InputFilter.LengthFilter
import android.util.AttributeSet
import android.widget.EditText
import com.ai.library_common.R
import kotlin.math.roundToInt

class CodeEditText @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null
) : EditText(context, attrs) {

    companion object {
        private const val DEFAULT_BOX_COUNT = 6
    }

    private val dp = context.resources.displayMetrics.density
    private var spacing = 8 * dp
    private var boxPadding = 8 * dp
    private var boxRatio = 1f
    private var stroke = 1 * dp
    private var radius = 5 * dp
    private var boxColor = Color.TRANSPARENT
    private var strokeColor = Color.GRAY
    private var focusColor = Color.CYAN

    private var boxWidth = 0f
    private var boxHeight = 0f
    private var boxCount = 0
    private val textWidths: FloatArray
    private val boxPaint: Paint
    private val strokePaint: Paint
    private val focusPaint: Paint

    private lateinit var onInputComplete: OnInputComplete
    fun setOnInputComplete(onInputComplete: OnInputComplete){
        this.onInputComplete=onInputComplete
    }
    init {
        background = null
        setPadding(0, 0, 0, 0)
        setTextIsSelectable(false)
        movementMethod = null
        isLongClickable = false
        isCursorVisible = false
        maxLines = 1
        isSingleLine = false
        setSelection(0)

        var maxLength = getMaxLength()
        if (maxLength == -1) {
            maxLength = DEFAULT_BOX_COUNT
            setMaxLength(maxLength)
        }
        boxCount = maxLength
        textWidths = FloatArray(maxLength)

        if (attrs != null) {
            val a = context.obtainStyledAttributes(attrs, R.styleable.CodeEditText)
            spacing = a.getDimension(R.styleable.CodeEditText_boxSpacing, spacing)
            boxPadding = a.getDimension(R.styleable.CodeEditText_boxPadding, boxPadding)
            radius = a.getDimension(R.styleable.CodeEditText_boxRadius, radius)
            boxRatio = a.getFloat(R.styleable.CodeEditText_boxRatio, boxRatio)
            stroke = a.getDimension(R.styleable.CodeEditText_boxStroke, stroke)
            boxColor = a.getColor(R.styleable.CodeEditText_boxColor, boxColor)
            strokeColor = a.getColor(R.styleable.CodeEditText_boxStrokeColor, strokeColor)
            focusColor = a.getColor(R.styleable.CodeEditText_boxStrokeFocusedColor, focusColor)
            a.recycle()
        }

        boxPaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
            it.color = boxColor
            it.style = Paint.Style.FILL
        }

        strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
            it.color = strokeColor
            it.style = Paint.Style.STROKE
            it.strokeWidth = stroke
        }

        focusPaint = Paint(Paint.ANTI_ALIAS_FLAG).also {
            it.color = focusColor
            it.style = Paint.Style.STROKE
            it.strokeWidth = stroke
        }
    }

    private fun getMaxLength(): Int {
        for (filter in filters) {
            if (filter is LengthFilter) {
                return filter.max
            }
        }
        return -1
    }

    private fun setMaxLength(max: Int) {
        filters = Array<InputFilter>(filters.size + 1) { i ->
            if (i == 0) LengthFilter(max) else filters[i - 1]
        }
        if (length() > max) {
            text = text.delete(max, length())
        }
    }

    override fun onSelectionChanged(selStart: Int, selEnd: Int) {
        if (text != null) {
            if (getMaxLength()==selEnd){
                onInputComplete.complete()
            }
            if (selStart != text.length || selEnd != text.length) {
                setSelection(text.length, text.length)
                return
            }
        }
        super.onSelectionChanged(selStart, selEnd)
    }

    /**
     * 测量规则
     * - at most:
     *      - 宽度:字体大小 + boxPadding + boxSpacing
     *      - 高度:宽度 / boxRatio
     * - exactly:
     *      - 宽度:控件宽度平分,忽略 boxPadding
     *      - 高度:控件高度
     */
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        if (boxCount == 0) {
            setMeasuredDimension(0, 0)
            return
        }

        val widthMode = MeasureSpec.getMode(widthMeasureSpec)
        val width: Float
        val totalSpacing = spacing * (boxCount - 1)
        if (widthMode == MeasureSpec.EXACTLY) {
            width = MeasureSpec.getSize(widthMeasureSpec).toFloat()
            boxWidth = (width - totalSpacing) / boxCount - stroke
        } else {
            boxWidth = textSize + boxPadding * 2
            width = (boxWidth + stroke) * boxCount + stroke * 0.5f + totalSpacing
        }

        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val height: Float
        if (heightMode == MeasureSpec.EXACTLY) {
            height = MeasureSpec.getSize(heightMeasureSpec).toFloat()
            boxHeight = height - stroke
        } else {
            boxHeight = boxWidth / boxRatio
            height = boxHeight + stroke * 1.5f
        }
        setMeasuredDimension((width + 0.5f).roundToInt(), (height + 0.5f).roundToInt())
    }

    override fun onDraw(canvas: Canvas) {
        val t = stroke
        val b = t + boxHeight
        var l = stroke
        for (i in 0 until boxCount) {
            val r = l + boxWidth
            canvas.drawRoundRect(l, t, r, b, radius, radius, boxPaint)
            if (hasFocus() && selectionStart == i) {
                canvas.drawRoundRect(l, t, r, b, radius, radius, focusPaint)
            } else {
                canvas.drawRoundRect(l, t, r, b, radius, radius, strokePaint)
            }
            paint.getTextWidths(text, 0, text.length, textWidths)
            paint.color = currentTextColor
            if (text.length > i) {
                val charX = l + boxWidth * 0.5f - textWidths[i] * 0.5f
                canvas.drawText(text, i, i + 1, charX, baseline.toFloat(), paint)
            }
            l = r + spacing + stroke
        }
    }

    interface OnInputComplete{
        fun complete()
    }
}

attrs:

 <declare-styleable name="CodeEditText">
        <attr name="boxSpacing" format="dimension" />
        <attr name="boxPadding" format="dimension" />
        <attr name="boxRadius" format="dimension"/>
        <attr name="boxRatio" format="float" />
        <attr name="boxStroke" format="dimension" />
        <attr name="boxColor" format="color" />
        <attr name="boxStrokeColor" format="color" />
        <attr name="boxStrokeFocusedColor" format="color" />
    </declare-styleable>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值