Android自定义方形验证码输入框

自定义VerifyCodeView

根据UI的分析来看,方形验证码输入框其实就是在EditText上面加入了一个个的矩形小方格,因此最简单的方式就是继承EditText。最终的效果图如下:
VerifyCodeView效果图

1.初始化Paint

根据上图,我们可以知道,需要有3种Paint:画普通边框、画当前选中边框、画闪烁的光标,因此我们第一步在构造方法中先初始化所需的Paint。

   private fun initPaint() {
        mNormalPaint.isAntiAlias = true
        mNormalPaint.color = mNormalColor
        mNormalPaint.style = Paint.Style.STROKE// 空心
        mNormalPaint.strokeWidth = mBorderWidth

        mSelectPaint.isAntiAlias = true
        mSelectPaint.color = mSelectColor
        mSelectPaint.style = Paint.Style.STROKE// 空心
        mSelectPaint.strokeWidth = mBorderWidth

        mCursorPaint.isAntiAlias = true
        mCursorPaint.color = mCursorColor
        mCursorPaint.style = Paint.Style.FILL_AND_STROKE
        mCursorPaint.strokeWidth = mCursorWidth
    }

2.自定义一些辅助属性,并完成初始化

当有了画笔工具之后,我们还需要知道其他一些属性,比如说验证码的个数、边框的厚度等等一些属性。

    var mFigures = 0// 验证码个数
    var mCodeMargin = 0// 验证码之间的间距
    var mSelectColor = 0// 选中框的颜色
    var mNormalColor = 0// 普通框的颜色
    var mBorderRadius = 0f// 边框直角的曲度
    var mBorderWidth = 0f// 边框的厚度
    var mCursorWidth = 0f// 光标宽度
    var mCursorColor = 0// 光标的颜色
    var mCursorDuration = 0L// 光标闪烁的时间

3.重写onMeasure方法

当完成一些基本字段的初始化后,就开始对自定义view进行测量,进而决定VerifyCodeView所占的大小。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        var widthResult = 0
        var heightResult = 0

        val widthMode = View.MeasureSpec.getMode(widthMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        if (widthMode == MeasureSpec.EXACTLY) {
            widthResult = widthSize
        } else {
            widthResult = getScreenWidth(context)
        }
        // 每个矩形的宽度
        mEachRectLength = (widthResult - (mFigures - 1) * mCodeMargin) / mFigures

        val heightMode = MeasureSpec.getMode(heightMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        if (heightMode == MeasureSpec.EXACTLY) {
            heightResult = heightSize
        } else {
            heightResult = mEachRectLength
        }

        setMeasuredDimension(widthResult, heightResult)
    }

4.重写onDraw方法

完成测量之后,我们就可以进行绘制了

	@SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        mCurrentPosition = text!!.length
        val width = mEachRectLength - paddingLeft - paddingRight
        val height = measuredHeight - paddingTop - paddingBottom
        for (i in 0 until mFigures) {
            canvas.save()
            val start = width * i + mCodeMargin * i + mBorderWidth
            var end = start + width - mBorderWidth
            if (i == mFigures - 1) {
                end -= mBorderWidth
            }
            // 画矩形选框
            val rect = RectF(start, mBorderWidth * 4, end, height.toFloat() - mBorderWidth)
            if (i == mCurrentPosition) {//选中的下一个状态
                canvas.drawRoundRect(rect, mBorderRadius, mBorderRadius, mSelectPaint)
            } else {
                canvas.drawRoundRect(rect, mBorderRadius, mBorderRadius, mNormalPaint)
            }
            canvas.restore()
        }

        // 绘制文字
        val value = text.toString()
        for (i in 0 until value.length) {
            canvas.save()
            val start = width * i + mCodeMargin * i
            val x = start + width / 2f// x
            paint.textAlign = Paint.Align.CENTER
            paint.color = currentTextColor
            val fontMetrics = paint.fontMetrics
            val baseline = (height - fontMetrics.bottom + fontMetrics.top) / 2 - fontMetrics.top
            canvas.drawText(value[i].toString(), x, baseline, paint)
            canvas.restore()
        }

        // 绘制光标
        if (!isCursorShowing && isCursorVisible && mCurrentPosition < mFigures && hasFocus()) {
            canvas.save()
            val startX = (width + mCodeMargin) * mCurrentPosition + width / 2f
            val startY = height / 4f
            val endX = startX
            val endY = height - height / 4f
            canvas.drawLine(startX, startY, endX, endY, mCursorPaint)
            canvas.restore()
        }

    }

5.解决键盘不弹出

这时我们运行VerifyCodeView,会发现键盘不会弹出,加入下面代码即可解决

	init {
        ...
        isFocusableInTouchMode = true// 让view的touch事件生效
    }

	override fun onTouchEvent(event: MotionEvent?): Boolean {
        event?.apply {
            if (action == MotionEvent.ACTION_DOWN) {
                requestFocus()
                setSelection(text!!.length)
                showKeyBoard(context)
                return false
            }
        }

        return super.onTouchEvent(event)
    }

6.让光标闪动起来

最后,还要让光标闪动起来

	// 控制光标闪烁
    var isCursorShowing = false
    var mCursorTimerTask: TimerTask? = null
    var mCursorTimer: Timer? = null

	init {
		...
		initCursorTimer()
	}
	
	private fun initCursorTimer() {
        mCursorTimerTask = object : TimerTask() {
            override fun run() {
                // 通过光标间歇性显示实现闪烁效果
                isCursorShowing = !isCursorShowing
                postInvalidate()
            }
        }
        mCursorTimer = Timer()
    }
    
 	override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        // 启动定时任务,定时刷新实现光标闪烁
        mCursorTimer?.scheduleAtFixedRate(mCursorTimerTask, 0, mCursorDuration)
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mCursorTimer?.cancel()
        mCursorTimer = null
    }

具体使用见demo
源码地址:https://github.com/LingChenJie/VerifyCodeView

好记性不如烂笔头,学习咱们这一行,还是要多动手,多记笔记。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值