欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android開(kāi)發(fā)自定義短信驗(yàn)證碼實(shí)現(xiàn)過(guò)程詳解

 更新時(shí)間:2023年06月22日 11:51:07   作者:龍之音  
這篇文章主要為大家介紹了Android開(kāi)發(fā)自定義短信驗(yàn)證碼實(shí)現(xiàn)過(guò)程詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

效果圖

簡(jiǎn)介

基本上只要需要登錄的APP,都會(huì)有驗(yàn)證碼輸入,所以說(shuō)是比較常用的控件,而且花樣也是很多的,這里列出來(lái)4種樣式,分別是:

  • 表格類(lèi)型
  • 方塊類(lèi)型
  • 橫線(xiàn)類(lèi)型
  • 圈圈類(lèi)型

其實(shí)還有很多其他的樣式,但是這四種是我遇到最多的樣式,所以特地拿來(lái)實(shí)現(xiàn)下,網(wǎng)上有很多類(lèi)似的輪子,實(shí)現(xiàn)方式也是蠻多的,比如說(shuō):

  • 組合控件(線(xiàn)性布局添加子View)
  • 自定義ViewGrop
  • 自定義View
  • ...

自己看了些網(wǎng)絡(luò)上實(shí)現(xiàn)的方案,參考了一些比較好的方式,這里先來(lái)分析下這個(gè)控件有哪些功能,再?zèng)Q定實(shí)現(xiàn)方案。

功能分析

  • 1、默認(rèn)狀態(tài)樣式展示
  • 2、支持設(shè)置最大數(shù)量
  • 3、支持4種類(lèi)型樣式
  • 4、點(diǎn)擊控件,彈出鍵盤(pán),獲取焦點(diǎn),顯示焦點(diǎn)樣式。焦點(diǎn)失去,展示默認(rèn)樣式。
  • 5、輸入數(shù)據(jù),焦點(diǎn)移動(dòng)到下一個(gè)位置,刪除數(shù)據(jù),焦點(diǎn)也跟隨移動(dòng)

通過(guò)功能4讓我第一想到的就是EditText控件,那么怎么做呢?大家都知道EditText有自己的樣式和操作,如果我們可以屏蔽無(wú)用的樣式和功能,留下我們需要的不就可以了嗎。

[圖片上傳失敗...(image-888083-1663139918159)]

EditText

  • 點(diǎn)擊EditText可以彈出鍵盤(pán)(需要的),并獲取焦點(diǎn)(需要的),顯示光標(biāo)(不需要的)
  • 長(zhǎng)按EditText會(huì)顯示復(fù)制,粘貼等操作(不需要的)
  • 輸入數(shù)據(jù),內(nèi)容默認(rèn)顯示(不需要的)

上面對(duì)EditText基本使用時(shí)出現(xiàn)的樣式和操作,有的是需要的,有的是不需要的,我們可以對(duì)不需要的進(jìn)行屏蔽,來(lái)代碼走起。

代碼實(shí)現(xiàn)

1、創(chuàng)建CodeEditText

繼承AppCompatEditText,并屏蔽一些功能。

class CodeEditText @JvmOverloads constructor(context: Context, var attrs: AttributeSet, var defStyleAttr: Int = 0) :
    AppCompatEditText(context, attrs, defStyleAttr) {
    init {
        initSetting()
    }
    private fun initSetting() {
        //內(nèi)容默認(rèn)顯示(不需要的)- 文字設(shè)置透明
        setTextColor(Color.TRANSPARENT)
        //觸摸獲取焦點(diǎn)
        isFocusableInTouchMode = true
        //不顯示光標(biāo)
        isCursorVisible = false
        //屏蔽長(zhǎng)按操作
        setOnLongClickListener { true }
    }
}

2、創(chuàng)建自定義配置參數(shù)

這里根據(jù)樣式,列舉一些參數(shù),如果需要其他參數(shù)可以自行添加

 <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" />
            <!--橫線(xiàn)-->
            <enum name="line" value="2" />
            <!--圓形-->
            <enum name="circle" value="3" />
            <!--TODO 拓展-->
        </attr>
        <!--code背景色-->
        <attr name="code_bg_color" format="color" />
        <!--邊框?qū)挾?->
        <attr name="code_border_width" format="dimension" />
        <!--邊框默認(rèn)顏色-->
        <attr name="code_border_color" format="color" />
        <!--邊框選中顏色-->
        <attr name="code_border_select_color" format="color" />
        <!--邊框圓角-->
        <attr name="code_border_radius" format="dimension" />
        <!--code 內(nèi)容顏色(密碼或文字)-->
        <attr name="code_content_color" format="color" />
        <!--code 內(nèi)容大?。艽a或文字)-->
        <attr name="code_content_size" format="dimension" />
        <!--code 單個(gè)寬度-->
        <attr name="code_item_width" format="dimension" />
        <!--code Item之間的間隙-->
        <attr name="code_item_space" format="dimension" />
    </declare-styleable>

3、獲取自定義配置參數(shù)

這里獲取參數(shù),有的參數(shù)默認(rèn)給了默認(rèn)值。

    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、重寫(xiě) onDraw 方法

   override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //當(dāng)前索引(待輸入的光標(biāo)位置)
        currentIndex = text?.length ?: 0
        //Item寬度(這里判斷如果設(shè)置了寬度并且合理就使用當(dāng)前設(shè)置的寬度,否則平均計(jì)算)
        codeItemWidth = if (codeItemWidth != -1 && (codeItemWidth * maxLength + codeItemSpace * (maxLength - 1)) <= measuredWidth) {
            codeItemWidth
        } else {
            ((measuredWidth - codeItemSpace * (maxLength - 1)) / maxLength).toInt()
        }
        //計(jì)算左右間距大小
        space = ((measuredWidth - codeItemWidth * maxLength - codeItemSpace * (maxLength - 1)) / 2).toInt()
        //繪制Code樣式
        when (codeStyle) {
            //表格
            0 -> {
                drawFormCode(canvas)
            }
            //方塊
            1 -> {
                drawRectangleCode(canvas)
            }
            //橫線(xiàn)
            2 -> {
                drawLineCode(canvas)
            }
            //圓形
            3 -> {
                drawCircleCode(canvas)
            }
        }
        //繪制文字
        drawContentText(canvas)
    }

在onDraw方法中主要是根據(jù)設(shè)置的codeStyle樣式,繪制不同的樣子。在繪制之前,主要做了三個(gè)操作。

  • 對(duì)當(dāng)前焦點(diǎn)索引currentIndex的計(jì)算
  • 單個(gè)驗(yàn)證碼寬度codeItemWidth的計(jì)算
  • 第一個(gè)驗(yàn)證碼距離左邊的間距space的計(jì)算

對(duì)當(dāng)前焦點(diǎn)索引currentIndex的計(jì)算

這里巧妙的使用了獲取當(dāng)前EditText數(shù)據(jù)的長(zhǎng)度作為當(dāng)前索引值,比如說(shuō),開(kāi)始沒(méi)有輸入數(shù)據(jù),獲取長(zhǎng)度為0,則當(dāng)前焦點(diǎn)應(yīng)該在0索引位置上,當(dāng)輸入一個(gè)數(shù)據(jù)時(shí),數(shù)據(jù)長(zhǎng)度為1,則焦點(diǎn)變?yōu)?,焦點(diǎn)相當(dāng)于移動(dòng)到了索引1的位置上,刪除數(shù)據(jù)同理,這樣就達(dá)到了上面分析的 ”功能5“的效果。

單個(gè)驗(yàn)證碼寬度codeItemWidth的計(jì)算

這里因?yàn)橛?中樣式,有的是表格一體展示,有的是分開(kāi)展示,比如方塊、橫線(xiàn)、圈圈,這三種中間是有空隙的,這個(gè)空隙大小我們做了配置參數(shù)code_item_space,對(duì)于這個(gè)參數(shù),表格樣式是不需要的,所以不管你設(shè)置了還是沒(méi)有設(shè)置,在表格樣式中是無(wú)效的。所以這里做了統(tǒng)一計(jì)算。

第一個(gè)驗(yàn)證碼距離左邊的間距space的計(jì)算

因?yàn)樾枰L制,所以需要起始點(diǎn),那么起點(diǎn)應(yīng)該是:(控件總寬度-所有驗(yàn)證碼的寬度-所有驗(yàn)證碼之前的空隙)/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)
        //繪制表格中間分割線(xiàn)
        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)
        }
        //繪制當(dāng)前位置邊框
        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
            }
            //當(dāng)前光標(biāo)樣式
            if (currentIndex != -1 && currentIndex == i && isCodeFocused) {
                canvas.drawBitmap(currentBitmap!!, left.toFloat(), 0f, mLinePaint)
            }
            //默認(rèn)樣式
            else {
                canvas.drawBitmap(defaultBitmap!!, left.toFloat(), 0f, mLinePaint)
            }
        }
    }

7、繪制橫線(xiàn)樣式

 /**
     * 橫線(xiàn) code
     */
    private fun drawLineCode(canvas: Canvas) {
        for (i in 0 until maxLength) {
            //當(dāng)前選中狀態(tài)
            if (currentIndex == i && isCodeFocused) {
                mLinePaint.color = borderSelectColor
            }
            //默認(rèn)狀態(tài)
            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) {
            //當(dāng)前繪制的圓圈的左x軸坐標(biāo)
            var left: Float = if (i == 0) {
                (space + i * codeItemWidth).toFloat()
            } else {
                space + i * codeItemWidth + codeItemSpace * i
            }
            //圓心坐標(biāo)
            val cx: Float = left + codeItemWidth / 2f
            val cy: Float = measuredHeight / 2f
            //圓形半徑
            val radius: Float = codeItemWidth / 5f
            //默認(rèn)樣式
            if (i >= currentIndex) {
                canvas.drawCircle(cx, cy, radius, mLinePaint.apply { style = Paint.Style.FILL })
            }
        }
    }

9、繪制輸入數(shù)據(jù)展示

    /**
     * 繪制內(nèi)容
     */
    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 拓展
                }
            }
        }
    }

上面就是對(duì)四種樣式的繪制,主要考察的API如下:

  • canvas.drawBitmap()
  • canvas.drawLine()
  • canvas.drawCircle()
  • canvas.drawText()

主要對(duì)這四個(gè)API的使用數(shù)據(jù)上的計(jì)算,相對(duì)比較的簡(jiǎn)單,其中有個(gè)點(diǎn)擊獲取焦點(diǎn)以及失去焦點(diǎn)更新樣式方式:

  override fun onFocusChanged(focused: Boolean, direction: Int, previouslyFocusedRect: Rect?) {
        super.onFocusChanged(focused, direction, previouslyFocusedRect)
        isCodeFocused = focused
        invalidate()
    }

通過(guò)isCodeFocused字段來(lái)控制。

10、控件使用

  <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

以上就是Android開(kāi)發(fā)自定義短信驗(yàn)證碼實(shí)現(xiàn)過(guò)程詳解的詳細(xì)內(nèi)容,更多關(guān)于Android自定義短信驗(yàn)證碼的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論