Android自定义密码框

需求

如下图,实现一个密码框,输入时显示实心圆。

思路

基于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>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值