自定义VerifyCodeView
根据UI的分析来看,方形验证码输入框其实就是在EditText上面加入了一个个的矩形小方格,因此最简单的方式就是继承EditText。最终的效果图如下:
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
好记性不如烂笔头,学习咱们这一行,还是要多动手,多记笔记。