開始閱讀本篇文章之前先來說一下使用場景吧,我們知道如今移動支付已經占據我們日常支付的90%的份額,以微信支付和支付寶支付為主,也越來越多的APP開始添加支付子產品,不管使用哪種支付有一個步驟是少不了的,那就是輸入支付密碼(指紋支付再此就不做讨論了哦),是以今天來給大家帶來一篇自定義支付密碼輸入框的設計和實作方式,同時記錄自己工作中遇到的問題及解決辦法。
github源碼位址傳送門
按照慣例我們先看看微信和支付寶支付密碼輸入框的樣式吧
微信支付密碼
支付寶支付密碼
看到這樣的效果相信很多開發者第一反應就是先網上搜一下看看有沒有現成的(哈哈,我也不例外哦),因為這都是簡單的一些view不涉及動畫是以網上相關例子還是很多的,我這邊總結了一下大緻可以分為一下幾類
1、通過布局的方式:
在布局裡邊放置6個EditView,在每個輸入框中間再放置一個view用于設定中間分割線,每個EditVIew隻允許輸入一個字元,然後對每個進行監聽,一個密碼輸入完之後讓另一個EditView擷取焦點,以此類推就可以大緻實作圖中的效果了(這樣确實可以實作,實作起來也很簡單,但是代碼量不少,而且這種方式是不是顯得逼格不夠高或者沒有逼格呐)
2、完全自定義view(繼承View):
這個就稍微複雜一點,大緻流程是,先監聽觸摸事件,按下時彈出鍵盤,然後對軟鍵盤進行監聽,擷取每次點選鍵盤對應的字元串,然後在onDraw方法裡邊畫6個圓,在繪制外邊框,然後是中間的分割線。這裡邊有個問題就是每次都要對軟鍵盤進行監聽取值等一系列操作,加上Android機型衆多整不好哪塊軟鍵盤就出問題了呐。(雖然有逼格,但是不實用哦)
欲步又止
3、繼承自EditView實作自定義view:
大緻流程和上一種差不多,不過我們不需要對軟鍵盤進行處理了,少了很多繁瑣及相容性的操作,同時又不失逼格,哈哈。
看到以上三種實作方式想必你大概已經知道我們要使用哪種方式實作了,沒錯就是內建EditView的自定義view,這樣我們還可以使用很多EditView的屬性哦
開發前先整理一下實作步驟:
1、繪制外邊框(可以是直角也可以是圓角,設計師要什麼我們就給他什麼)
2、繪制密碼之間的分割線(豎線)
3、繪制實心圓代替輸入的字元
4、對輸入字元進行監聽,便于擴充處理
5、實作一些常用的外部接口方法調用
具體實作
1、繪制外邊框:
要想繪制邊框我們首先要知道view的寬高,通過onSizeChanged方法去初始化寬高等資料,然後繪制圓角矩形(預設讓他矩形顯示直接傳入圓角半徑為0即可)
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
height = h;
width = w;
divideLineWStartX = w / maxCount;
startX = w / maxCount / 2; //第一個圓心的x坐标
startY = h / 2; //第一個圓心的y坐标
bottomLineLength = w / (maxCount + 2);
rectF.set(0, 0, width, height);
}
RectF rectF = new RectF()
rectF.set(0, 0, width, height);
canvas.drawRoundRect(rectF, rectAngle, rectAngle, borderPaint);
2、繪制密碼之間的分割線:
既然是分割線肯定是等均分的,假設我們的密碼最大輸入maxCount=6,那麼我們隻需畫5個分割線就可以了,分割線坐标的計算
計算分割線的起點和終點的坐标
通過循環畫出每個分割線
for (int i = 0; i < maxCount - 1; i++) {
canvas.drawLine((i + 1) * divideLineWStartX,
0,
(i + 1) * divideLineWStartX,
height,
divideLinePaint);
}
完成這一步我們先運作一下看看邊框效果吧
模拟器上運作效果
3、繪制實心圓代替輸入的字元:
這裡需要監聽EditView的輸入,重寫onTextChanged方法擷取輸入字元的長度,然後計算每個圓圓心的坐标位置
//第一個圓心的x坐标
startX = w / maxCount / 2;
//第一個圓心的y坐标
startY = h / 2;
private void drawPsdCircle(Canvas canvas) {
for (int i = 0; i < textLength; i++) {
canvas.drawCircle(startX + i * 2 * startX,
startY,
radius,
circlePaint);
}
}
寫到這裡的時候是不是感覺樣式問題已經完成的差不多了,運作以來輸入幾個字元串一看,MD出問題了(看圖說話)
Paste_Image.png
從圖中可以看出是繪制了相應的實心圓,但是自帶的底部線、光标、字元還在,要是拿這個去交差絕逼會被産品罵死。
産品的内心獨白
出現這個問題肯定是代碼的問題喽,我們根據問題去一個一個解決,首先給view設定一個透明的背景色,然後隐藏光标,再跑一下看看
this.setBackgroundColor(Color.TRANSPARENT);
this.setCursorVisible(false);
Paste_Image.png
這次底部的線和光标都見了,但是輸入的字元還在,這又是什麼問題???我們明明重寫了onDraw方法,怎麼還會出現原來的字元呐,等等。。。對啊,我們隻是重寫,他肯定還有自己的方法,我們隻要把EditView内部重繪的方法幹掉不就行了,想到這裡喜出望外,拿跟辣條先壓壓驚,在ondraw方法中這樣做
@Override
protected void onDraw(Canvas canvas) {
//不删除的話會預設繪制輸入的文字
// super.onDraw(canvas);
}
你沒看錯,就是這一行代碼注釋掉就ok,
至于是為什麼你肯定知道,
不注釋的話在我們重寫之前他已經調用了内部方法
去繪制輸入的字元了,
我們在重寫後雖然我們的方法生效了,
但它的方法也生效了哦。
此時壓抑不住内心的小激動趕緊運作起來看看(哈哈,完美解決問題)
Paste_Image.png
至此主要功能已經完成,剩下的需要去封裝一些方法供外部調用,我這裡已經封裝幾個方法,我們知道這樣設定支付密碼的頁面一般有兩個:一個設定密碼,一個重新設定密碼,按照正常的邏輯我們去監聽這個密碼輸入框,輸入密碼之後進行比較看是否相等就完事了,為了友善以後使用不要每次自己再去寫一大堆監聽方法,我們直接在内部封裝好是不是對以後使用起來更友善一點呐
public interface onPasswordListener {
void onDifference();
void onEqual(String psd);
}
//使用者需要調用的方法
public void setComparePassword(String comparePassword, onPasswordListener listener) {
mComparePassword = comparePassword;
mListener = listener;
}
這裡就直接上代碼了,代碼通俗易懂
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
super.onTextChanged(text, start, lengthBefore, lengthAfter);
textLength = text.toString().length();
if (mComparePassword != null && textLength == maxCount) {
if (TextUtils.equals(mComparePassword, getPasswordString())) {
mListener.onEqual(getPasswordString());
} else {
mListener.onDifference();
}
}
invalidate();
}
實際使用中我們這樣設定(是不是瞬間感覺用的過程簡單了很多)
passwordInputView.setComparePassword("123456", new PayPsdInputView.onPasswordListener() {
@Override
public void onDifference() {
// TODO: 2017/5/7 和上次輸入的密碼不一緻 做相應的業務邏輯處理
Toast.makeText(MainActivity.this,"兩次密碼輸入不同",Toast.LENGTH_SHORT).show();
}
@Override
public void onEqual(String psd) {
// TODO: 2017/5/7 兩次輸入密碼相同,那就去進行支付樓
Toast.makeText(MainActivity.this,"密碼相同"+psd,Toast.LENGTH_SHORT).show();
}
});
文章到此本應該結束了,可是我們UI設計師給出的效果圖不是這樣子的,不按常理出牌(心中頓時飄過一萬隻草泥馬)
草泥馬
來看看我們的效果圖
Paste_Image.png
大哥,算了算了,還是去給他實作一下吧
他不按常量出牌,不過這也是他們一貫的作風,既然他們要這樣的效果那我們就去做喽,整個流程還是一樣的,唯一的不同就是外邊框和密碼之間的分割線變成了底部間斷的線,這肯定難不倒我們啦,不就是畫六條線嗎,每根線的起點終點坐标和上邊圓心左邊計算差不多,就不多描述了看代碼最實在
private void drawBottomBorder(Canvas canvas) {
for (int i = 0; i < maxCount; i++) {
cX = startX + i * 2 * startX;
canvas.drawLine(cX - bottomLineLength / 2,
height,
cX + bottomLineLength / 2,
height, bottomLinePaint);
}
}
項目至此完美收工,看看效果吧
MD你要的效果給你.png
最後總結
以上微信支付密碼和我們這種現實效果我都封裝在PayPsdInputView中了,可以根據需求切換不同的樣式,
Paste_Image.png
如果以後還要其他的支付密碼輸入的樣式的話同樣會添加進來的,目的隻有一個--------->下次開發省時省力。
兩種樣式供你選擇
番外篇
我相信看到這裡肯定有一部分小夥伴會說MDZZ,這不就是簡單的畫矩形、畫圓、畫線嗎,有什麼好寫的,誰都會做。我想說的是你們說的沒錯,涉及的知識點是很簡單,但是不要忘了,麻雀雖小五腑俱全,真正你去一行一行敲的時候你會發現有很多不曾注意過的問題都會浮出水面。隻是單純的會幾個知識點其實沒什麼卵用,把所學知識點運用起來重組成一個功能子產品的時候你才算真正的掌握。
來,老表,抽根煙,平複一下暴躁的心情
謹以此篇來記錄自己項目中遇到的問題,獻給需要類似功能的小夥伴們。如果你有好的建議歡迎評論指出,大家一起讨論、學習、進步!