文字的字体颜色、大小、样式直接设置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>
网友评论