Android-自界说短信验证码

藏宝库编辑 2024-9-16 23:58:35 36 0 来自 中国
效果图

简介

根本上只要必要登录的APP,都会有验证码输入,所以说是比力常用的控件,而且格式也是许多的,这里列出来4种样式,分别是:

  • 表格类型
  • 方块类型
  • 横线类型
  • 圈圈类型
着实尚有许多其他的样式,但是这四种是我碰到最多的样式,所以特地拿来实现下,网上有许多类似的轮子,实现方式也是蛮多的,比如说:

  • 组合控件(线性结构添加子View)
  • 自界说ViewGrop
  • 自界说View
  • ...
自己看了些网络上实现的方案,参考了一些比力好的方式,这里先来分析下这个控件有哪些功能,再决定实现方案。
功能分析


  • 1、默认状态样式展示
  • 2、支持设置最大数量
  • 3、支持4种类型样式
  • 4、点击控件,弹出键盘,获取焦点,体现焦点样式。焦点失去,展示默认样式。
  • 5、输入数据,焦点移动到下一个位置,删除数据,焦点也跟随移动
通过功能4让我第一想到的就是EditText控件,那么怎么做呢?各人都知道EditText有自己的样式和利用,假如我们可以屏蔽无用的样式和功能,留下我们必要的不就可以了吗。
[图片上传失败...(image-888083-1663139918159)]
EditText

  • 点击EditText可以弹出键盘(必要的),并获取焦点(必要的),体现光标(不必要的)
  • 长按EditText会体现复制,粘贴等利用(不必要的)
  • 输入数据,内容默认体现(不必要的)
上面对EditText根本使用时出现的样式和利用,有的是必要的,有的是不必要的,我们可以对不必要的举行屏蔽,来代码走起。
代码实现

1、创建CodeEditText

继续AppCompatEditText,并屏蔽一些功能。
class CodeEditText @JvmOverloads constructor(context: Context, var attrs: AttributeSet, var defStyleAttr: Int = 0) :    AppCompatEditText(context, attrs, defStyleAttr) {    init {        initSetting()    }    private fun initSetting() {        //内容默认体现(不必要的)- 笔墨设置透明        setTextColor(Color.TRANSPARENT)        //触摸获取焦点        isFocusableInTouchMode = true        //不体现光标        isCursorVisible = false        //屏蔽长按利用        setOnLongClickListener { true }    }}2、创建自界说设置参数

这里根据样式,枚举一些参数,假如必要其他参数可以自行添加
<declare-styleable name="CodeEditText">        <!--code模式-->        <attr name="code_mode" format="enum">            <!--笔墨-->            <enum name="text" value="0" />            <!--TODO 拓展-->        </attr>        <!--code样式-->        <attr name="code_style" format="enum">            <!--表格-->            <enum name="form" value="0" />            <!--方块-->            <enum name="rectangle" value="1" />            <!--横线-->            <enum name="line" value="2" />            <!--圆形-->            <enum name="circle" value="3" />            <!--TODO 拓展-->        </attr>        <!--code配景致-->        <attr name="code_bg_color" format="color" />        <!--边框宽度-->        <attr name="code_border_width" format="dimension" />        <!--边框默认颜色-->        <attr name="code_border_color" format="color" />        <!--边框选中颜色-->        <attr name="code_border_select_color" format="color" />        <!--边框圆角-->        <attr name="code_border_radius" format="dimension" />        <!--code 内容颜色(暗码或笔墨)-->        <attr name="code_content_color" format="color" />        <!--code 内容巨细(暗码或笔墨)-->        <attr name="code_content_size" format="dimension" />        <!--code 单个宽度-->        <attr name="code_item_width" format="dimension" />        <!--code Item之间的间隙-->        <attr name="code_item_space" format="dimension" />    </declare-styleable>3、获取自界说设置参数

这里获取参数,有的参数默认给了默认值。
    private fun initAttrs() {        val obtainStyledAttributes = context.obtainStyledAttributes(attrs, R.styleable.CodeEditText)        codeMode = obtainStyledAttributes.getInt(R.styleable.CodeEditText_code_mode, 0)        codeStyle = obtainStyledAttributes.getInt(R.styleable.CodeEditText_code_style, 0)        borderWidth = obtainStyledAttributes.getDimension(R.styleable.CodeEditText_code_border_width, DensityUtil.dip2px(context, 1.0f))        borderColor = obtainStyledAttributes.getColor(R.styleable.CodeEditText_code_border_color, Color.GRAY)        borderSelectColor = obtainStyledAttributes.getColor(R.styleable.CodeEditText_code_border_select_color, Color.GRAY)        borderRadius = obtainStyledAttributes.getDimension(R.styleable.CodeEditText_code_border_radius, 0f)        codeBgColor = obtainStyledAttributes.getColor(R.styleable.CodeEditText_code_bg_color, Color.WHITE)        codeItemWidth = obtainStyledAttributes.getDimension(R.styleable.CodeEditText_code_item_width, -1f).toInt()        codeItemSpace = obtainStyledAttributes.getDimension(R.styleable.CodeEditText_code_item_space, DensityUtil.dip2px(context, 16f))        if (codeStyle == 0) codeItemSpace = 0f        codeContentColor = obtainStyledAttributes.getColor(R.styleable.CodeEditText_code_content_color, Color.GRAY)        codeContentSize = obtainStyledAttributes.getDimension(R.styleable.CodeEditText_code_content_size, DensityUtil.dip2px(context, 16f))        obtainStyledAttributes.recycle()    }4、重写 onDraw 方法

   override fun onDraw(canvas: Canvas) {        super.onDraw(canvas)        //当前索引(待输入的光标位置)        currentIndex = text?.length ?: 0        //Item宽度(这里判断假如设置了宽度而且公道就使用当前设置的宽度,否则匀称盘算)        codeItemWidth = if (codeItemWidth != -1 && (codeItemWidth * maxLength + codeItemSpace * (maxLength - 1)) <= measuredWidth) {            codeItemWidth        } else {            ((measuredWidth - codeItemSpace * (maxLength - 1)) / maxLength).toInt()        }        //盘算左右间距巨细        space = ((measuredWidth - codeItemWidth * maxLength - codeItemSpace * (maxLength - 1)) / 2).toInt()        //绘制Code样式        when (codeStyle) {            //表格            0 -> {                drawFormCode(canvas)            }            //方块            1 -> {                drawRectangleCode(canvas)            }            //横线            2 -> {                drawLineCode(canvas)            }            //圆形            3 -> {                drawCircleCode(canvas)            }        }        //绘制笔墨        drawContentText(canvas)    }在onDraw方法中重要是根据设置的codeStyle样式,绘制差别的样子。在绘制之前,重要做了三个利用。

  • 对当前焦点索引currentIndex的盘算
  • 单个验证码宽度codeItemWidth的盘算
  • 第一个验证码隔断左边的间距space的盘算
对当前焦点索引currentIndex的盘算

这里奥妙的使用了获取当前EditText数据的长度作为当前索引值,比如说,开始没有输入数据,获取长度为0,则当前焦点应该在0索引位置上,当输入一个数据时,数据长度为1,则焦点变为1,焦点相当于移动到了索引1的位置上,删除数据同理,如许就达到了上面分析的 ”功能5“的效果。
单个验证码宽度codeItemWidth的盘算

这里由于有4中样式,有的是表格一体展示,有的是分开展示,比如方块、横线、圈圈,这三种中心是有清闲的,这个清闲巨细我们做了设置参数code_item_space,对于这个参数,表格样式是不必要的,所以不管你设置了还是没有设置,在表格样式中是无效的。所以这里做了同一盘算。
第一个验证码隔断左边的间距space的盘算

由于必要绘制,所以必要起始点,那么出发点应该是:(控件总宽度-全部验证码的宽度-全部验证码之前的清闲)/2 .
5、绘制表格样式

    /**     * 表格code     */    private fun drawFormCode(canvas: Canvas) {        //绘制表格边框        defaultDrawable.setBounds(space, 0, measuredWidth - space, measuredHeight)        defaultBitmap = CodeHelper.drawableToBitmap(defaultDrawable, measuredWidth - 2 * space, measuredHeight)        canvas.drawBitmap(defaultBitmap!!, space.toFloat(), 0f, mLinePaint)        //绘制表格中心分割线        for (i in 1 until maxLength) {            val startX = space + codeItemWidth * i + codeItemSpace * i            val startY = 0f            val stopY = measuredHeight            canvas.drawLine(startX, startY, startX, stopY.toFloat(), mLinePaint)        }        //绘制当前位置边框        for (i in 0 until maxLength) {            if (currentIndex != -1 && currentIndex == i && isCodeFocused) {                when (i) {                    0 -> {                        val radii = floatArrayOf(borderRadius, borderRadius, 0f, 0f, 0f, 0f, borderRadius, borderRadius)                        currentDrawable.cornerRadii = radii                        currentBitmap =                            CodeHelper.drawableToBitmap(currentDrawable, (codeItemWidth + borderWidth / 2).toInt(), measuredHeight)                    }                    maxLength - 1 -> {                        val radii = floatArrayOf(0f, 0f, borderRadius, borderRadius, borderRadius, borderRadius, 0f, 0f)                        currentDrawable.cornerRadii = radii                        currentBitmap =                            CodeHelper.drawableToBitmap(currentDrawable, (codeItemWidth + borderWidth / 2 + codeItemSpace).toInt(), measuredHeight)                    }                    else -> {                        val radii = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)                        currentDrawable.cornerRadii = radii                        currentBitmap = CodeHelper.drawableToBitmap(currentDrawable, (codeItemWidth + borderWidth).toInt(), measuredHeight)                    }                }                val left = if (i == 0) (space + codeItemWidth * i) else ((space + codeItemWidth * i + codeItemSpace * i) - borderWidth / 2)                canvas.drawBitmap(currentBitmap!!, left.toFloat(), 0f, mLinePaint)            }        }    }6、绘制方块样式

/**     * 方块 code     */    private fun drawRectangleCode(canvas: Canvas) {        defaultDrawable.cornerRadius = borderRadius        defaultBitmap = CodeHelper.drawableToBitmap(defaultDrawable, codeItemWidth, measuredHeight)        currentDrawable.cornerRadius = borderRadius        currentBitmap = CodeHelper.drawableToBitmap(currentDrawable, codeItemWidth, measuredHeight)        for (i in 0 until maxLength) {            val left = if (i == 0) {                space + i * codeItemWidth            } else {                space + i * codeItemWidth + codeItemSpace * i            }            //当前光标样式            if (currentIndex != -1 && currentIndex == i && isCodeFocused) {                canvas.drawBitmap(currentBitmap!!, left.toFloat(), 0f, mLinePaint)            }            //默认样式            else {                canvas.drawBitmap(defaultBitmap!!, left.toFloat(), 0f, mLinePaint)            }        }    }7、绘制横线样式

/**     * 横线 code     */    private fun drawLineCode(canvas: Canvas) {        for (i in 0 until maxLength) {            //当前选中状态            if (currentIndex == i && isCodeFocused) {                mLinePaint.color = borderSelectColor            }            //默认状态            else {                mLinePaint.color = borderColor            }            val startX: Float = space + codeItemWidth * i + codeItemSpace * i            val startY: Float = measuredHeight - borderWidth            val stopX: Float = startX + codeItemWidth            val stopY: Float = startY            canvas.drawLine(startX, startY, stopX, stopY, mLinePaint)        }    }8、绘制圈圈样式

/**     * 圆形 code     */    private fun drawCircleCode(canvas: Canvas) {        for (i in 0 until maxLength) {            //当前绘制的圆圈的左x轴坐标            var left: Float = if (i == 0) {                (space + i * codeItemWidth).toFloat()            } else {                space + i * codeItemWidth + codeItemSpace * i            }            //圆心坐标            val cx: Float = left + codeItemWidth / 2f            val cy: Float = measuredHeight / 2f            //圆形半径            val radius: Float = codeItemWidth / 5f            //默认样式            if (i >= currentIndex) {                canvas.drawCircle(cx, cy, radius, mLinePaint.apply { style = Paint.Style.FILL })            }        }    }10、绘制输入数据展示

    /**     * 绘制内容     */    private fun drawContentText(canvas: Canvas) {        val textStr = text.toString()        for (i in 0 until maxLength) {            if (textStr.isNotEmpty() && i < textStr.length) {                when (codeMode) {                    //笔墨                    0 -> {                        val code: String = textStr.toString()                        val textWidth: Float = mTextPaint.measureText(code)                        val textHeight: Float = CodeHelper.getTextHeight(code, mTextPaint)                        val x: Float = space + codeItemWidth * i + codeItemSpace * i + (codeItemWidth - textWidth) / 2                        val y: Float = (measuredHeight + textHeight) / 2f                        canvas.drawText(code, x, y, mTextPaint)                    }                    //TODO 拓展                }            }        }    }上面就是对四种样式的绘制,重要观察的API如下:

  • canvas.drawBitmap()
  • canvas.drawLine()
  • canvas.drawCircle()
  • canvas.drawText()
重要对这四个API的使用数据上的盘算,相对比力的简单,此中有个点击获取焦点以及失去焦点更新样式方式:
  override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {        super.onFocusChanged(focused, direction, previouslyFocusedRect)        isCodeFocused = focused        invalidate()    }通过isCodeFocused字段来控制。
11、控件使用

  <com.yxlh.androidxy.demo.ui.codeet.widget.CodeEditText        android:layout_width="match_parent"        android:layout_height="50dp"        android:layout_marginLeft="20dp"        android:layout_marginTop="40dp"        android:layout_marginRight="20dp"        android:inputType="number"        android:maxLength="4"        app:code_border_color="@android:color/darker_gray"        app:code_border_radius="5dp"        app:code_border_select_color="@color/design_default_color_primary"        app:code_border_width="2dp"        app:code_content_color="@color/purple_500"        app:code_content_size="35dp"        app:code_item_width="50dp"        app:code_mode="text"        app:code_bg_color="#E1E1E1"        app:code_style="rectangle" />GitHub链接:
https://github.com/yixiaolunhui/AndroidXY
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-11-22 02:07, Processed in 0.165254 second(s), 32 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表