您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“Android怎么實現(xiàn)自定義密碼輸入框”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Android怎么實現(xiàn)自定義密碼輸入框”吧!
預(yù)期效果圖:
如上圖所示,要實現(xiàn)一個這種密碼輸入框的樣式,原生并未提供類似的效果,所以需要自定義控件的方式實現(xiàn)。
預(yù)期的基礎(chǔ)效果:
只接受數(shù)字;
支持輸入加密顯示;
支持刪除;
密碼位數(shù)可配置;
文字大小、顏色、數(shù)字框背景可配置;
方案分析:
需要解決的問題:
配置性;
輸入、刪除如何實現(xiàn)?
整體UI如何實現(xiàn)?
1.對于輸入刪除可以通過setOnKeyListener監(jiān)聽軟件盤的事件。
2.可配置性數(shù)據(jù)可以通過自定義的屬性文件配置;
3.對于UI效果:
A:可以基于原生控件做開發(fā),每一個數(shù)字布局對應(yīng)一個TextView,選用數(shù)據(jù)結(jié)構(gòu)對其管理,再選用一種容器布局,比如LinearLayout進行添加。
B:通過自定義View的方式開發(fā),需要自行繪制,繪制的內(nèi)容包括背景、及密碼內(nèi)容、密碼加密樣式內(nèi)容。
這里選用方案B的方式進行實現(xiàn),盡量使用較少的控件去實現(xiàn),使用A的方案至少要用到5個原生控件的組合。
1.繼承ViewGrop還是View?
如果選用方案A的話其實算是繼承了ViewGrop,而內(nèi)部的單個數(shù)字則作為一個獨立的子控件,這樣的話是可以繼承ViewGrop的
,但顯然不需要這么麻煩(需要處理layout等),這個密碼輸入就是一個獨立的控件不需要再加入子控件,所以直接繼承View。
class PasswordEditText @JvmOverloads constructor( context: Context, attributeSet: AttributeSet? = null, ) : View(context, attributeSet, 0) { }
2.繼承View的話就要處理 wrap_content,所以要重寫onMeasure,即在未設(shè)置具體的寬度時也要能夠正常的顯示測量。先定義一些寬高顏色的變量:
//密碼位數(shù) private var passwordLength = 4 private var textColor = 0 //間隔 -> dp2px private var itemPadding = 5 //單個數(shù)字包括背景寬度 dp2px private var itemWidth = 30 private var bgItemColor = 0 private val mPaintBg = Paint() //用于存儲輸入后的密碼 private val password = arrayOfNulls<String>(passwordLength)
寬度的話相對來說還是很好計算的:
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圖基本可以算出來了,不涉及太復雜的計算,這里并未對高度進行處理,理論上高度的值應(yīng)該用指定的就好了;
需要做的測量基本就是這些了,下面開始繪制背景了:
也很簡單根據(jù) passwordLength 循環(huán)繪制圓角矩形就OK了,而參數(shù)的話也很好計算出來:
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) } }
為了讓效果更明顯,先畫了一個顏色鮮艷的:
背景的話就繪制OK了,下面要做的就是監(jiān)聽事件再繪制密碼內(nèi)容了;
要實現(xiàn)一個OnKeyListener
//鍵盤監(jiān)聽 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) { //確認鍵 return@OnKeyListener true } } return@OnKeyListener false }
這里只是添加好了回調(diào)條件,還要做相應(yīng)的處理。但鍵盤還沒彈出,所以要先處理點擊事件調(diào)用系統(tǒng)的方法主動彈出軟鍵盤才行
override fun onTouchEvent(event: MotionEvent?): Boolean { if (event!!.action == MotionEvent.ACTION_DOWN) { //獲取焦點 requestFocus() //getContext().getSystemService(Context.INPUT_METHOD_SERVICE) inputManager.showSoftInput(this, InputMethodManager.SHOW_FORCED) return true } return super.onTouchEvent(event) }
重寫onTouchEvent之后就可以攔截點擊事件彈出鍵盤拉。而View和軟鍵盤的聯(lián)系需要通過onCreateInputConnection 來實現(xiàn),具體可以看下源碼的介紹。
下面接著處理監(jiān)聽事件,首先是在接受到數(shù)字輸入時的處理,要把輸入的數(shù)字存儲到容器并繪制出來
這里需要注意keyCode 的值,看下源碼并不是KEYCODE_0 就是0了
再存儲一下輸入的數(shù)字:
if (keyCode >= KeyEvent.KEYCODE_0 && keyCode <= KeyEvent.KEYCODE_9) { //數(shù)字鍵 password[currentInputPosition] = (keyCode - 7).toString() currentInputPosition++ postInvalidate() return@OnKeyListener true }
currentInputPosition為了標記當前操作的位置,方便添加和刪除,因為都是從兩頭開始的用這個值就可以了。
下面開始繪制文字了,如果加密的話只需要在每個背景的中心畫一個小黑點就OK了,或者直接畫一個數(shù)字,根據(jù)基線用drawText畫就好了,而Y軸的基線很好確定就是高度的一半像素減去高度文字一半,通過設(shè)置textAlign = Paint.Align.CENTER即可實現(xiàn)橫向上的居中(會根據(jù)X基線),X軸的基線則需要計算一下,比如第一個框的X基線則應(yīng)該是,框?qū)挼囊话朐贉p去繪制文字寬度的一半,這樣才能在中間,第二個框內(nèi)X的基線應(yīng)該是:paddingLeft+1框?qū)?1padding+框?qū)?2
所以繪制文字的代碼就出來了:
//繪制文字 private fun drawPasswordNumber(canvas: Canvas) { for (i in password.indices) { if (password[i] != null) { //沒有開啟明文顯示,繪制密碼密文 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 ) } } }
這里加了是否開啟密文顯示的開關(guān),最終運行的效果如下:
這個黑點也可以通過畫圓的方式進行繪制,但無法通過字符串進行動態(tài)配置。
再輸入完四位以后在點的話就會數(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ù)組中保存的已輸入密碼,更新操作標記位,再刷新繪制就OK了
if (keyCode == KeyEvent.KEYCODE_DEL) { //刪除 if(currentInputPosition == 0){ return@OnKeyListener true } password[currentInputPosition-1] = null currentInputPosition-- postInvalidate() return@OnKeyListener true }
看下最后的UI效果:
下面可以提供一些對外的方法、接口,比如獲取內(nèi)容,輸入刪除確認的回調(diào)監(jiān)聽。
//獲取輸入內(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ù)開發(fā)其他一些主流的樣式,比如下劃線、網(wǎng)格的樣式,但繪制思路基本相同,還可以加上一個任務(wù)類執(zhí)行繪制光標的操作。
到此,相信大家對“Android怎么實現(xiàn)自定義密碼輸入框”有了更深的了解,不妨來實際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進入相關(guān)頻道進行查詢,關(guān)注我們,繼續(xù)學習!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。