美文网首页
自定义密码输入框View

自定义密码输入框View

作者: Android小豆渣 | 来源:发表于2022-08-31 18:46 被阅读0次
    先看看效果图~是不是你的菜 1661942722043.jpg

    文字的字体颜色、大小、样式直接设置textview的属性就行。较好的就是加入个当前输入的位置的变色。不过我们需求没这个就没加上了。想更多支持的就自己添加~这里我的需求就加了这点。
    代码:

    class EditPassWordView(context: Context, attrs: AttributeSet?) : AppCompatTextView(context, attrs) {
        //背景画笔
        private val mPaintBg = Paint(Paint.ANTI_ALIAS_FLAG)
    
        //文字画笔
        private val mPaintTv = TextPaint(Paint.ANTI_ALIAS_FLAG)
    
        //密码位数
        private var passwordLength = 4
    
        //间隔  ->   dp2px
        private var itemSpace = 5
    
        //单个数字包括背景宽度 dp2px
        private var itemWidth = 30
    
        //item背景颜色
        private var itemBackgroundColor = 0
    
        //item圆角大小
        private var itemRound = 5f
    
        //是否加密显示
        private var isCipherEnable = false
    
        //加密显示的文字
        private var cipherString = "*"
    
        //用于存储输入后的密码
        private val password = arrayOfNulls<String>(passwordLength)
    
        //当前输入位数
        private var currentInputPosition = 0
    
        //软键盘管理器
        private var inputManager: InputMethodManager? =
            context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager?
    
        //键盘监听
        private var keyListener = OnKeyListener { _, keyCode, event ->
            val action = event.action
            if (action == KeyEvent.ACTION_DOWN) {
                if (keyCode == KeyEvent.KEYCODE_DEL) {
                    //删除
                    if (currentInputPosition == 0) {
                        return@OnKeyListener true
                    }
                    password[currentInputPosition - 1] = null
                    currentInputPosition--
                    postInvalidate()
                    operationListener?.deleteOperationCallBack()
                    return@OnKeyListener true
                }
                if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
                    //数字键
                    inputPassWord((keyCode - 7).toString())
                    return@OnKeyListener true
                }
                if (keyCode == KeyEvent.KEYCODE_ENTER) {
                    //确认键
                    return@OnKeyListener true
                }
            }
    
            return@OnKeyListener false
        }
    
        init {
            val attributes = context.obtainStyledAttributes(attrs, R.styleable.EditPassWordView)
            itemBackgroundColor =
                attributes.getColor(R.styleable.EditPassWordView_itemBackgroundColor, Color.WHITE)
            itemRound = attributes.getDimension(R.styleable.EditPassWordView_itemRound, 5f)
            itemSpace = attributes.getDimension(R.styleable.EditPassWordView_itemSpace, 5f).toInt()
            isCipherEnable = attributes.getBoolean(R.styleable.EditPassWordView_isCipherEnable, false)
            passwordLength = attributes.getInt(R.styleable.EditPassWordView_passwordLength, 4)
            attributes.recycle()
    
            isFocusableInTouchMode = true
    
            mPaintBg.color = itemBackgroundColor
    
            mPaintTv.textSize = textSize
            mPaintTv.color = textColors.defaultColor
            mPaintTv.typeface = typeface
    
    
            setOnKeyListener(keyListener)
            //数字键盘得用这个监听
            doOnTextChanged { text, start, _, _ ->
                val inputNumber = text.toString()
                val newNumber = inputNumber.substring(start, inputNumber.length)
                inputPassWord(newNumber)
            }
        }
    
        override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
            var width = 0
            when (MeasureSpec.getMode(widthMeasureSpec)) {
                MeasureSpec.UNSPECIFIED, MeasureSpec.AT_MOST -> {
                    width = itemWidth * passwordLength + itemSpace * (passwordLength - 1)
                }
    
                MeasureSpec.EXACTLY -> {
                    width = MeasureSpec.getSize(widthMeasureSpec)
                    itemWidth = (width - itemSpace * (passwordLength - 1)) / passwordLength
                }
            }
            setMeasuredDimension(width, itemWidth)
        }
    
        override fun onDraw(canvas: Canvas) {
            drawBgItems(canvas)
            drawPasswordNumber(canvas)
        }
    
        override fun onTouchEvent(event: MotionEvent?): Boolean {
            if (event?.action == MotionEvent.ACTION_DOWN) {
                //获取焦点
                requestFocus()
                inputManager?.showSoftInput(this, InputMethodManager.SHOW_FORCED)
                return true
            }
            return super.onTouchEvent(event)
        }
    
        //绘制背景
        private fun drawBgItems(canvas: Canvas) {
            for (i in password.indices) {
                //注意padding的影响
                val rect = RectF(
                    (i * itemWidth + (i) * itemSpace + paddingStart).toFloat(),
                    0f + paddingTop,
                    ((i + 1) * itemWidth + i * itemSpace + paddingEnd).toFloat(),
                    (itemWidth + paddingBottom).toFloat()
                )
    
                canvas.drawRoundRect(rect, itemRound, itemRound, mPaintBg)
            }
        }
    
        private lateinit var rectTv: Rect
    
        //绘制文字
        private fun drawPasswordNumber(canvas: Canvas) {
            for (i in password.indices) {
                if (password[i] != null) {
                    //没有开启明文显示,绘制密码密文
                    val txt = if (isCipherEnable) cipherString else password[i]
                    //计算文字的宽度
                    rectTv = Rect(
                        (i * itemWidth + (i) * itemSpace + paddingStart),
                        0 + paddingTop,
                        ((i + 1) * itemWidth + i * itemSpace + paddingEnd),
                        itemWidth + paddingBottom
                    )
                    mPaintTv.getTextBounds(txt.toString(), 0, txt!!.length, rectTv)
                    val offset = (rectTv.top + rectTv.bottom) / 2
                    canvas.drawText(
                        password[i]!!,
                        (paddingLeft + itemWidth * i + itemSpace * i + (itemWidth - rectTv.width()) / 2).toFloat(),
                        (paddingTop + itemWidth / 2).toFloat() - offset, mPaintTv
                    )
                }
            }
        }
    
    
        private fun inputPassWord(number: String) {
            //加入判断 currentInputPosition
            if (currentInputPosition == passwordLength) {
                return
            }
            password[currentInputPosition] = number
            currentInputPosition++
            postInvalidate()
            //判断是否输入完成还是输入中
            if (currentInputPosition == passwordLength) {
                operationListener?.completeOperationCallBack(getTextContent())
            } else {
                operationListener?.inputOperationCallBack()
            }
        }
    
        //获取输入内容
        fun getTextContent(): String {
            val sb = StringBuilder()
            for (p in password) {
                p?.let {
                    sb.append(p)
                }
            }
            return sb.toString()
        }
    
        fun reset() {
            for (i in 0 until passwordLength) {
                password[i] = null
            }
            currentInputPosition = 0
            postInvalidate()
        }
    
        private var operationListener: OperationListener? = null
    
        fun setOperationListener(operationListener: OperationListener) {
            this.operationListener = operationListener
        }
    
        interface OperationListener {
            fun inputOperationCallBack()
    
            fun completeOperationCallBack(password: String)
    
            fun deleteOperationCallBack()
        }
    }
    

    自定义的styleable

        <declare-styleable name="EditPassWordView">
            <attr name="itemBackgroundColor" format="color" />
            <attr name="itemRound" format="dimension" />
            <attr name="itemSpace" format="dimension" />
            <attr name="isCipherEnable" format="boolean" />
            <attr name="passwordLength" format="integer" />
        </declare-styleable>
    

    相关文章

      网友评论

          本文标题:自定义密码输入框View

          本文链接:https://www.haomeiwen.com/subject/ifgdnrtx.html