需求
如下图,实现一个密码框,输入时显示实心圆。
思路
基于Edittext实现密码框,动态计算整体控件宽高,绘制方框格以及实心圆,根据输入的文本长度填充对应个数的实心圆。
注意点:需要重写onSelectionChanged()方法,避免用户点击密码框时光标定位错误,导致回退时删错密文。由于比较简单,以下直接贴代码了。
实现
1、主代码:
class InputPwdEditText @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
EditText(context, attrs) {
private var pwRectF = RectF()
private var lineWidth = DpFitter.get(context).dp(0.5f)
private var singleSize = DpFitter.get(context).dp(10) //单个密码输入框的宽高
private var textCount = 6 //输入密码长度e
private var corner = 0f //输入框边缘的圆角
private var pwRadius = DpFitter.get(context).dp(3) //密码的半径
private var listener: PwdListener? = null
//绘制边框的画笔
private val bgPaint by lazy {
val paint = Paint()
paint.style = Paint.Style.STROKE
paint.isAntiAlias = true
paint.isDither = true
paint.color = Color.parseColor("#DBDDE1")
paint.strokeWidth = lineWidth.toFloat()
paint
}
private val pwPaint by lazy {
val paint = Paint()
paint.style = Paint.Style.FILL
paint.isAntiAlias = true
paint.isDither = true
paint.color = Color.parseColor("#ff000000")
paint
}
init {
val ta = context.obtainStyledAttributes(attrs, R.styleable.InputPwdEditText)
textCount = ta.getInt(R.styleable.InputPwdEditText_pwCount, textCount)
singleSize = ta.getDimension(R.styleable.InputPwdEditText_size, singleSize.toFloat()).toInt()
corner = ta.getFloat(R.styleable.InputPwdEditText_bgCorner, corner)
ta.recycle()
inputType = EditorInfo.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD //限制输入数字
isCursorVisible = false //不显示光标
background = null
setPadding(0, 0, 0, 0)//必须设置,EditText自带padding
filters = Array<InputFilter>(1) { InputFilter.LengthFilter(textCount) }
//键盘自动弹起,由于dialog刚show时InputPwdEditText还未初始化,所以需要延时
postDelayed({
requestFocus()
val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
}, 300)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
val width = singleSize * textCount + lineWidth * (2 + textCount - 1)
val height = singleSize + lineWidth * 2
setMeasuredDimension(width, height)
}
override fun onSelectionChanged(selStart: Int, selEnd: Int) {
setSelection(text.length)
}
override fun onDraw(canvas: Canvas) {
drawBg(canvas)
drawPw(canvas)
listener?.invoke(text.toString(), text.length == textCount)
}
/**
* 绘制密码实心圆
*/
private fun drawPw(canvas: Canvas) {
val text = text.toString()
for (i in text.indices) {
val x = singleSize * i + lineWidth * (i + 1) + singleSize / 2f
canvas.drawCircle(x, height / 2f, pwRadius.toFloat(), pwPaint)
}
}
/**
* 绘制密码边框背景
*/
private fun drawBg(canvas: Canvas) {
pwRectF.set(lineWidth.toFloat(), lineWidth.toFloat(), (width - lineWidth).toFloat(), (height - lineWidth).toFloat())
if (corner == 0f) {
canvas.drawRect(pwRectF, bgPaint)
} else {
canvas.drawRoundRect(pwRectF, corner, corner, bgPaint)
}
for (i in 1 until textCount) {
val x = singleSize * i + lineWidth * i + lineWidth / 2f
canvas.drawLine(x, lineWidth.toFloat(), x, height.toFloat() - lineWidth, bgPaint)
}
}
fun addListener(listener: PwdListener) {
this.listener = listener
}
}
typealias PwdListener = (pw: String, full: Boolean) -> Unit
2、attrs:
<declare-styleable name="InputPwdEditText">
<attr name="bgCorner" format="float" />
<attr name="pwCount" format="integer" />
<attr name="size" format="dimension" />
</declare-styleable>