Android-短信验证码

效果图

alt
简介

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

  • 表格类型
  • 方块类型
  • 横线类型
  • 圈圈类型

其实还有很多其他的样式,但是这四种是我遇到最多的样式,所以特地拿来实现下,网上有很多类似的轮子,实现方式也是蛮多的,比如说:

  • 组合控件(线性布局添加子View)
  • 自定义ViewGrop
  • 自定义View
  • ...

自己看了些网络上实现的方案,参考了一些比较好的方式,这里先来分析下这个控件有哪些功能,再决定实现方案。

功能分析

  • 1、默认状态样式展示
  • 2、支持设置最大数量
  • 3、支持4种类型样式
  • 4、点击控件,弹出键盘,获取焦点,显示焦点样式。焦点失去,展示默认样式。
  • 5、输入数据,焦点移动到下一个位置,删除数据,焦点也跟随移动

通过功能4让我第一想到的就是EditText控件,那么怎么做呢?大家都知道EditText有自己的样式和操作,如果我们可以屏蔽无用的样式和功能,留下我们需要的不就可以了吗。

alt

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[i].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

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/4476.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

SmS-Activate一款好用的短信验证码接收工具

前言 有些国外应用在使用应用上的功能时需要注册账号&#xff0c;由于某种不可抗因素&#xff0c;我们的手机号一般不支持注册&#xff0c;接收不到信息验证码&#xff0c;于是我们可以使用sms-activate提供的服务&#xff0c;实惠又稳定&#xff0c;最近sms-activate.org网站…

发送短信验证码+登陆功能

业务&#xff1a; 手机端点击发送验证码&#xff0c;调用第三方平台&#xff08;我们用的是“任信了”平台&#xff09;的接口&#xff0c;去给手机发短信验证码。 过程&#xff1a; 代码&#xff1a; /*** 发送短信验证码 * param json 前台传入电话号码* return 返回发…

ChatGPT专业应用:生成新闻稿

正文共 949 字&#xff0c;阅读大约需要 4 分钟 媒体工作者必备技巧&#xff0c;您将在4分钟后获得以下超能力&#xff1a; 生成新闻稿 Beezy评级 &#xff1a;A级 *经过寻找和一段时间的学习&#xff0c;一部分人能掌握。主要提升效率并增强自身技能。 推荐人 | Kim 编辑者…

ChatGPT专业应用:创作对联

正文共 727 字&#xff0c;阅读大约需要 3 分钟 文化工作者必备技巧&#xff0c;您将在3分钟后获得以下超能力&#xff1a; 创作对联 Beezy评级 &#xff1a;B级 *经过简单的寻找&#xff0c; 大部分人能立刻掌握。主要节省时间。 推荐人 | Kim 编辑者 | Yolanda ●此图片由L…

如何高效性的使用ChatGPT

目录 简单介绍 考虑以下几点 举几个例子 简单介绍 ChatGPT是由OpenAI开发的一种自然语言处理模型&#xff0c;它基于GPT&#xff08;Generative Pre-trained Transformer&#xff09;架构&#xff0c;旨在为用户提供更加流畅、准确的对话体验。 ChatGPT通过在大规模的互联网…

Claude在线AI注册教程(免费、可平替Chatgpt)

Claude 是Anthropic公司开发的AI聊天机器人&#xff0c;与ChatGPT类似&#xff0c;由OpenAI前副总裁创办。虽然比不上GPT4&#xff0c;但在连续对话能力、写小说、编写代码、解释概念等方面表现出色 Claude 目前还处于免费使用阶段&#xff0c;只需注册一个Slack账号即可以&am…

「ChatGPT最强竞品」爆火:不限量不要钱免注册!一手实测体验在此

点击上方“视学算法”&#xff0c;选择加"星标"或“置顶” 重磅干货&#xff0c;第一时间送达 金磊 丰色 发自 凹非寺量子位 | 公众号 QbitAI 家人们&#xff0c;终于&#xff01;终于&#xff01; 免费、支持中文、无需注册、不用骚操作直接可用的类ChatGPT产品&…

AIGC——使用Cursor对比直接使用chatGPT有哪些技巧

Cursor等chatGPT相关工具的那些用法 1. 聊聊it团队经常面临的问题2. 试想一些AI辅助的应用场景3. 看回现实可用的工具及其使用3.1 加载文件作为对话上下文3.2 源码工程辅助阅读3.3 批量文件式输入口3.4 规范核查工具3.5 写PPT3.6 口述业务流程或逻辑生成持续图流程图 4 通用思路…

ChatGPT 创业:如何用人工智能 AI 开一家赚钱的公司

这是一篇演示如何使用 ChatGPT prompt &#xff08;提示词&#xff09;的文章。 在了解 ChatGPT 的文案写作能力后&#xff0c;我产生了一个念头&#xff0c;如果让它来写一篇命题作文&#xff0c;会是怎样&#xff1f; Prompt 提示词&#xff0c;和人工智能 AI 对话中一个重要…

如何高效使用 ChatGPT?

最近 ChatGPT 非常火。如果你刷社交媒体&#xff0c;大概已经被各种消息搞得头晕脑胀。从大佬到普通人都对这个话题表现出了浓厚的兴趣&#xff0c;就连 ChatGPT 为主题的学术研讨会通知我都已经收到 3 份了。 参与到这个话题里自然是有好处的&#xff0c;例如可以更容易获取流…

2023 ChatGPT v3.5 谷歌浏览器插件

重要的事情说三遍&#xff1a;免费 免费 免费 安装步骤&#xff1a; 1. 电脑下载谷歌浏览器&#xff0c;如果有&#xff0c;请忽略此步。 2. 下载插件 3. 打开谷歌浏览器&#xff0c;开启右上角开发者模式 4. 将下载的插件解压后&#xff0c;进入子目录&#xff0c;拖入浏…

【chatgpt插件-ChatGPT Sidebar】

ChatGPT Sidebar google chrome插件搜索

ChatGPT插件:让你的 ChatGPT 与众不同!

这个 Chrome 浏览器插件是作者觉得原本的 ChatGPT 界面不太丰富&#xff0c;然后想着自己写一个插件把它变得更加好看一点 &#x1f92a;&#xff0c;因此把这个插件取名为 ChatGPT-theme&#xff0c;目前插件已经发布了是 1.0.1 版本的&#xff0c;因为 1.0.0 作者测了一下有些…

这个 Chrome 插件,让你的 ChatGPT 不再报错

ChatGPT的官网最近几天报错越来越频繁了&#xff0c;相信大家都发现了。 一旦你离开页面时间比较久&#xff0c;再度返回跟它进行对话&#xff0c;就会出现如下报错&#xff1a; 虽然这个报错信息以前也出现过&#xff0c;但现在的频率确实过高&#xff0c;对于每天需要使用 C…

谷歌验证码reCAPTCHA的运用

写在前面 提示&#xff1a;reCAPTCHA因为依赖谷歌网站验证&#xff0c;在国内是无法使用的。但是谷歌提供了国内接口&#xff0c;将google.com替换为recaptcha.net即可。 reCAPTCHA在国内的接口示例&#xff1a;https://www.recaptcha.net/recaptcha/api2/demo 本文中对于所有…

谷歌验证码recaptcha接入

recaptcha是一项谷歌免费验证码服务&#xff0c;本次接入的是reCaptcha v2 1、首先选择reCaptcha的类型&#xff0c;注册申请api密钥对&#xff0c;包含站点密钥和密钥组成&#xff0c;站点密钥用于在网站调用reCAPTCHA服务&#xff0c;密钥则是验证应用后端和 reCAPTCHA 服务校…

技巧|使用Chatgpt在聊天界面直接显示公式

ChatGPT的使用已经越来越多&#xff0c;从问问题到写代码&#xff0c;但是有些时候&#xff0c;当我们问一些数学问题的时候&#xff0c;由于要显示公式&#xff0c;ChatGPT会直接给我们输出Latex代码。有Latex代码当然是好&#xff0c;我们可以把它放到编辑器编辑即可。 但是说…

python实现ChatGPT连续多轮对话

代码如下。注意&#xff0c;api_key需要改成自己的密钥才能成功运行。 import openaiapi_key "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"openai.api_key api_keydef askChatGPT(messages):MODEL "gpt-3.5-turbo"response openai.ChatCom…

用这三本书,探究 ChatGPT 的底层逻辑

最近爆火的ChatGPT能够很好地理解人类的语言&#xff0c;并以流畅且符合逻辑的自然语言来反馈。刚刚发布的GPT-4更是在各种学科考试&#xff08;美国高考SAT、研究生入学考试GRE等&#xff09;和专业考试&#xff08;律师考试等&#xff09;中能拿到前10%的成绩。某种程度上&am…

超火的ChatGPT技术原理与我们关系

文章目录 1 ChatGPT简介2 ChatGPT发展历程3 ChatGPT原理4 ChatGPT与我们关系4.1 ChatGPT与大数据关系4.2 ChatGPT与Java关系 4.3 ChatGPT时代机遇4.4 ChatGPT存在的问题4.5 ChatGPT发展思考 1 ChatGPT简介 众所周知&#xff0c;最近&#xff0c;由美国人工智能公司OpenAI推出的…