亚洲乱码中文字幕综合,中国熟女仑乱hd,亚洲精品乱拍国产一区二区三区,一本大道卡一卡二卡三乱码全集资源,又粗又黄又硬又爽的免费视频

Android自定義密碼輸入框的簡(jiǎn)單實(shí)現(xiàn)過(guò)程

 更新時(shí)間:2021年11月09日 14:56:34   作者:Aramis_twoY  
在最近的項(xiàng)目中,用戶需要輸入密碼,不想用系統(tǒng)鍵盤,就寫(xiě)了一個(gè)自定義鍵盤,下面這篇文章主要給大家介紹了關(guān)于Android自定義密碼輸入框的簡(jiǎn)單實(shí)現(xiàn)過(guò)程,需要的朋友可以參考下

一、實(shí)現(xiàn)效果及方案

預(yù)期效果圖:


如上圖所示,要實(shí)現(xiàn)一個(gè)這種密碼輸入框的樣式,原生并未提供類似的效果,所以需要自定義控件的方式實(shí)現(xiàn)。

預(yù)期的基礎(chǔ)效果:

只接受數(shù)字;

支持輸入加密顯示;

支持刪除;

密碼位數(shù)可配置;

文字大小、顏色、數(shù)字框背景可配置;

方案分析:

需要解決的問(wèn)題:

配置性;

輸入、刪除如何實(shí)現(xiàn)?

整體UI如何實(shí)現(xiàn)?

1.對(duì)于輸入刪除可以通過(guò)setOnKeyListener監(jiān)聽(tīng)軟件盤的事件。

2.可配置性數(shù)據(jù)可以通過(guò)自定義的屬性文件配置;

3.對(duì)于UI效果:

A:可以基于原生控件做開(kāi)發(fā),每一個(gè)數(shù)字布局對(duì)應(yīng)一個(gè)TextView,選用數(shù)據(jù)結(jié)構(gòu)對(duì)其管理,再選用一種容器布局,比如LinearLayout進(jìn)行添加。

B:通過(guò)自定義View的方式開(kāi)發(fā),需要自行繪制,繪制的內(nèi)容包括背景、及密碼內(nèi)容、密碼加密樣式內(nèi)容。

二、實(shí)現(xiàn)

這里選用方案B的方式進(jìn)行實(shí)現(xiàn),盡量使用較少的控件去實(shí)現(xiàn),使用A的方案至少要用到5個(gè)原生控件的組合。

1.繼承ViewGrop還是View?

如果選用方案A的話其實(shí)算是繼承了ViewGrop,而內(nèi)部的單個(gè)數(shù)字則作為一個(gè)獨(dú)立的子控件,這樣的話是可以繼承ViewGrop的
,但顯然不需要這么麻煩(需要處理layout等),這個(gè)密碼輸入就是一個(gè)獨(dú)立的控件不需要再加入子控件,所以直接繼承View。

class PasswordEditText @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet? = null,
) : View(context, attributeSet, 0) {
    
}

2.繼承View的話就要處理 wrap_content,所以要重寫(xiě)onMeasure,即在未設(shè)置具體的寬度時(shí)也要能夠正常的顯示測(cè)量。先定義一些寬高顏色的變量:

 //密碼位數(shù)
    private var passwordLength = 4
    private var textColor = 0

    //間隔  ->   dp2px
    private var itemPadding = 5

    //單個(gè)數(shù)字包括背景寬度 dp2px
    private var itemWidth = 30
    private var bgItemColor = 0
    private val mPaintBg = Paint()
    //用于存儲(chǔ)輸入后的密碼
    private val password = arrayOfNulls<String>(passwordLength)

寬度的話相對(duì)來(lái)說(shuō)還是很好計(jì)算的:

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        var width = 0
        when (MeasureSpec.getMode(widthMeasureSpec)) {
            MeasureSpec.UNSPECIFIED,MeasureSpec.AT_MOST ->{
                width = itemWidth * passwordLength + itemPadding * (passwordLength-1)
            }

            MeasureSpec.EXACTLY ->{
                width = MeasureSpec.getSize(widthMeasureSpec)
                itemWidth = (width - itemPadding *(passwordLength -1)) / passwordLength
            }
        }
        setMeasuredDimension(width,itemWidth)
    }

看著UI圖基本可以算出來(lái)了,不涉及太復(fù)雜的計(jì)算,這里并未對(duì)高度進(jìn)行處理,理論上高度的值應(yīng)該用指定的就好了;
需要做的測(cè)量基本就是這些了,下面開(kāi)始繪制背景了:

也很簡(jiǎn)單根據(jù) passwordLength 循環(huán)繪制圓角矩形就OK了,而參數(shù)的話也很好計(jì)算出來(lái):

private fun drawBgItems(canvas: Canvas) {
        for (i in password.indices) {
            未處理padding值 加上即可
            val rect = RectF(
                (i * itemWidth + (i) * itemPadding).toFloat(),
                0f,
                ((i+1) * itemWidth + i * itemPadding).toFloat(),
                itemWidth.toFloat()
            )
            canvas.drawRoundRect(rect, 5f, 5f, mPaintBg)
        }
    }

為了讓效果更明顯,先畫(huà)了一個(gè)顏色鮮艷的:


背景的話就繪制OK了,下面要做的就是監(jiān)聽(tīng)事件再繪制密碼內(nèi)容了;

要實(shí)現(xiàn)一個(gè)OnKeyListener

 //鍵盤監(jiān)聽(tīng)
    private val keyListener = OnKeyListener { v, keyCode, event ->
        val action = event.action
        if (action == KeyEvent.ACTION_DOWN) {
            if (keyCode == KeyEvent.KEYCODE_DEL) {
                //刪除
                return@OnKeyListener true
            }
            if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
                 //數(shù)字鍵

            }
            if (keyCode == KeyEvent.KEYCODE_ENTER) {
                //確認(rèn)鍵
                return@OnKeyListener true
            }
        }

        return@OnKeyListener false
    }

這里只是添加好了回調(diào)條件,還要做相應(yīng)的處理。但鍵盤還沒(méi)彈出,所以要先處理點(diǎn)擊事件調(diào)用系統(tǒng)的方法主動(dòng)彈出軟鍵盤才行

 override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (event!!.action == MotionEvent.ACTION_DOWN) {
            //獲取焦點(diǎn)
            requestFocus()
            //getContext().getSystemService(Context.INPUT_METHOD_SERVICE)
            inputManager.showSoftInput(this, InputMethodManager.SHOW_FORCED)
            return true
        }
        return super.onTouchEvent(event)
    }

重寫(xiě)onTouchEvent之后就可以攔截點(diǎn)擊事件彈出鍵盤拉。而View和軟鍵盤的聯(lián)系需要通過(guò)onCreateInputConnection 來(lái)實(shí)現(xiàn),具體可以看下源碼的介紹。

下面接著處理監(jiān)聽(tīng)事件,首先是在接受到數(shù)字輸入時(shí)的處理,要把輸入的數(shù)字存儲(chǔ)到容器并繪制出來(lái)

這里需要注意keyCode 的值,看下源碼并不是KEYCODE_0 就是0了


再存儲(chǔ)一下輸入的數(shù)字:

 if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
                 //數(shù)字鍵
                password[currentInputPosition] = (keyCode - 7).toString()
                currentInputPosition++
                postInvalidate()
                return@OnKeyListener true
            }

currentInputPosition為了標(biāo)記當(dāng)前操作的位置,方便添加和刪除,因?yàn)槎际菑膬深^開(kāi)始的用這個(gè)值就可以了。

下面開(kāi)始繪制文字了,如果加密的話只需要在每個(gè)背景的中心畫(huà)一個(gè)小黑點(diǎn)就OK了,或者直接畫(huà)一個(gè)數(shù)字,根據(jù)基線用drawText畫(huà)就好了,而Y軸的基線很好確定就是高度的一半像素減去高度文字一半,通過(guò)設(shè)置textAlign = Paint.Align.CENTER即可實(shí)現(xiàn)橫向上的居中(會(huì)根據(jù)X基線),X軸的基線則需要計(jì)算一下,比如第一個(gè)框的X基線則應(yīng)該是,框?qū)挼囊话朐贉p去繪制文字寬度的一半,這樣才能在中間,第二個(gè)框內(nèi)X的基線應(yīng)該是:paddingLeft+1框?qū)?1padding+框?qū)?2

所以繪制文字的代碼就出來(lái)了:

 //繪制文字
    private fun drawPasswordNumber(canvas: Canvas) {
        for (i in password.indices) {
            if (password[i] != null) {
                //沒(méi)有開(kāi)啟明文顯示,繪制密碼密文
                val txt = if (isCipherEnable) cipherString else password[i]
                mPaintTv.getTextBounds(txt, 0, txt!!.length, rectTv)
                val offset = (rectTv.top + rectTv.bottom) / 2
                canvas.drawText(
                    password[i]!!,
                    (paddingLeft + itemWidth * i + itemPadding * i + itemWidth / 2).toFloat(),
                    (paddingTop + itemWidth / 2).toFloat() - offset, mPaintTv
                )
            }
        }
    }

這里加了是否開(kāi)啟密文顯示的開(kāi)關(guān),最終運(yùn)行的效果如下:



這個(gè)黑點(diǎn)也可以通過(guò)畫(huà)圓的方式進(jìn)行繪制,但無(wú)法通過(guò)字符串進(jìn)行動(dòng)態(tài)配置。

再輸入完四位以后在點(diǎn)的話就會(huì)數(shù)組越界閃退了,所以在完成相應(yīng)位數(shù)的添加后要禁止再繪制。

if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) {
                //加入判斷 currentInputPosition 
                if (currentInputPosition == passwordLength) {
                    return@OnKeyListener true
                }
                password[currentInputPosition] = (keyCode - 7).toString()
                currentInputPosition++
                postInvalidate()
                return@OnKeyListener true
            }

下面要處理刪除操作了,刪除要做的就是去除數(shù)組中保存的已輸入密碼,更新操作標(biāo)記位,再刷新繪制就OK了

if (keyCode == KeyEvent.KEYCODE_DEL) {
                //刪除
                if(currentInputPosition == 0){
                    return@OnKeyListener true
                }
                password[currentInputPosition-1] = null
                currentInputPosition--
                postInvalidate()
                return@OnKeyListener true
            }

看下最后的UI效果:


下面可以提供一些對(duì)外的方法、接口,比如獲取內(nèi)容,輸入刪除確認(rèn)的回調(diào)監(jiān)聽(tīng)。

//獲取輸入內(nèi)容
    fun getTextContent():String {
        val sb = StringBuilder()
        for (p in password) {
            p?.let {
                sb.append(p)
            }
        }
        return sb.toString()
    }

    //操作回調(diào) 加些需要的參數(shù)
    interface OperationListener{
        fun inputOperationCallBack()
        
        fun completeOperationCallBack()
        
        fun deleteOperationCallBack()
    }

這里還可以繼續(xù)開(kāi)發(fā)其他一些主流的樣式,比如下劃線、網(wǎng)格的樣式,但繪制思路基本相同,還可以加上一個(gè)任務(wù)類執(zhí)行繪制光標(biāo)的操作。

總結(jié)

一個(gè)簡(jiǎn)單的自定義View Demo,自定義View的難點(diǎn)在于參數(shù)的計(jì)算和很多API一不用就會(huì)忘記,但是繼承ViewGroup還是View,測(cè)量繪制布局的過(guò)程以及基本的繪制方法配置還是要清楚些,或者說(shuō)能想起來(lái),對(duì)于太復(fù)雜難以開(kāi)發(fā)的控件感覺(jué)如果有現(xiàn)成的還是可以直接用的,畢竟沒(méi)那么多時(shí)間去調(diào)試一些復(fù)雜的參數(shù),如果能寫(xiě)出來(lái)也很牛皮吧。

相關(guān)文章

最新評(píng)論