序言:在上周的項目中,需要做一個密碼鎖的功能,然后密碼下面還得有鍵盤,就類似支付寶支付的時候那樣:
當然了,我們項目的需求簡單點,純數字的就可以了,然后上周就百度了自定義鍵盤,隨便找了一個修改修改就用到項目中去了。
多謝這位簡友:[Android] 自定義輸入支付密碼的軟鍵盤
今天自己抽空寫了一個自定義View的鍵盤控件,下面跟大家分享一下:
####思路: 1、布局:
(1)、宮格:我們可以將這個布局看成是宮格布局,然后需要計算出每個小宮格在屏幕中的位置(坐標),然后再用canvas畫出相應的矩形即可。
(2)、數字:我們需要計算出每個小宮格中的中心點位置(坐標),然后用canvas畫數字上去,當然,我們知道最后一個是刪除鍵,并不是數字,我們可以準備一張圖片,將圖片畫到相應的位置。
復制代碼
2、用戶動作:
(1)、按下:用戶每一次按下的時候就表示這一次動作的開始,所以首先要將各種標識位(自定義所需要的標識位)設置成初始狀態,然后需要記錄按下的坐標,然后計算出用戶按下的坐標與宮格中哪個點相對應,在記錄相應數字。最后刷新布局
(2)、抬起:用戶抬起的時候需要刷新布局,然后將按下過程中記錄下的數字或者刪除鍵信息通過接口的形式回調給用戶,最后恢復標識位
(3)、取消:將所有的標識位恢復成初始狀態。
復制代碼
好了,思路就講到這里,我們來看看onDraw方法:
protected void onDraw(Canvas canvas) {if (!isInit) {initData();}mPaint.setColor(Color.WHITE);//畫宮格//第一排canvas.drawRoundRect(10, mHeight / 2 + 10, 10 + mRectWidth, mHeight / 2 + 10 + mRectHeight, 10, 10, mPaint);canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 10, 20 + 2 * mRectWidth, mHeight / 2 + 10 + mRectHeight, 10, 10, mPaint);canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 10, 30 + 3 * mRectWidth, mHeight / 2 + 10 + mRectHeight, 10, 10, mPaint);//第二排canvas.drawRoundRect(10, mHeight / 2 + 20 + mRectHeight, 10 + mRectWidth, mHeight / 2 + 20 + 2 * mRectHeight, 10, 10, mPaint);canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 20 + mRectHeight, 20 + 2 * mRectWidth, mHeight / 2 + 20 + 2 * mRectHeight, 10, 10, mPaint);canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 20 + mRectHeight, 30 + 3 * mRectWidth, mHeight / 2 + 20 + 2 * mRectHeight, 10, 10, mPaint);//第三排canvas.drawRoundRect(10, mHeight / 2 + 30 + 2 * mRectHeight, 10 + mRectWidth, mHeight / 2 + 30 + 3 * mRectHeight, 10, 10, mPaint);canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 30 + 2 * mRectHeight, 20 + 2 * mRectWidth, mHeight / 2 + 30 + 3 * mRectHeight, 10, 10, mPaint);canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 30 + 2 * mRectHeight, 30 + 3 * mRectWidth, mHeight / 2 + 30 + 3 * mRectHeight, 10, 10, mPaint);//第四排mPaint.setColor(Color.GRAY);canvas.drawRoundRect(10, mHeight / 2 + 40 + 3 * mRectHeight, 10 + mRectWidth, mHeight / 2 + 40 + 4 * mRectHeight, 10, 10, mPaint);mPaint.setColor(Color.WHITE);canvas.drawRoundRect(20 + mRectWidth, mHeight / 2 + 40 + 3 * mRectHeight, 20 + 2 * mRectWidth, mHeight / 2 + 40 + 4 * mRectHeight, 10, 10, mPaint);mPaint.setColor(Color.GRAY);canvas.drawRoundRect(30 + 2 * mRectWidth, mHeight / 2 + 40 + 3 * mRectHeight, 30 + 3 * mRectWidth, mHeight / 2 + 40 + 4 * mRectHeight, 10, 10, mPaint);mPaint.setColor(Color.BLACK);mPaint.setTextSize(60);// 設置字體大小mPaint.setStrokeWidth(2);//畫數字//第一排canvas.drawText("1", xs[0], ys[0], mPaint);canvas.drawText("2", xs[1], ys[0], mPaint);canvas.drawText("3", xs[2], ys[0], mPaint);//第二排canvas.drawText("4", xs[0], ys[1], mPaint);canvas.drawText("5", xs[1], ys[1], mPaint);canvas.drawText("6", xs[2], ys[1], mPaint);//第三排canvas.drawText("7", xs[0], ys[2], mPaint);canvas.drawText("8", xs[1], ys[2], mPaint);canvas.drawText("9", xs[2], ys[2], mPaint);//第四排canvas.drawText(".", xs[0], ys[3], mPaint);canvas.drawText("0", xs[1], ys[3], mPaint);canvas.drawBitmap(mBpDelete, xs[2] - mWidthOfBp / 2 + 10, ys[3] - mHeightOfBp / 2 - 10, mPaint);
}
復制代碼
注:上面的坐標需要我們自己算出,耐心一點,很容易算的,你只需要搞清楚在Android中屏幕是怎樣的坐標系就可以了。坐標算出來之后,我們先畫宮格,然后再畫數字和刪除鍵,這里有人要問了,我可以先畫數字嗎,NO,因為當你畫完數字之后再畫宮格你會發現數字不見了,為什么呢?**被你的宮格擋住了 >_<**所以千萬別這樣做。畫的過程中別忘了將畫筆設置一些你需要的屬性。
好了,畫好之后,我們來看看效果怎么樣:
樣式出來了哈!但是設計的沒那么好看,大家將就看一看哈>_<
然后我們需要重寫onTouch事件,在里面判斷用戶的一些行為:
switch (event.getAction()) {case MotionEvent.ACTION_DOWN: //按下//恢復默認值setDefault();/***判斷按下的點在哪個宮格中*/invalidate();//刷新界面return true;case MotionEvent.ACTION_UP: //彈起invalidate();//刷新界面/***一次按下結束,返回點擊的數字*///恢復默認setDefault();return true;case MotionEvent.ACTION_CANCEL: //取消//恢復默認值setDefault();return true;}
復制代碼
如上面偽代碼所示,我寫了一個方法來判斷按下的點在哪個宮格中:
private void handleDown(float x, float y) {if (y < mHeight / 2) {return;}if (x >= 10 && x <= 10 + mRectWidth) { //第一列clickX = xs[0];if (y >= mHeight / 2 + 10 && y <= mHeight / 2 + 10 + mRectHeight) { //第一排(1)clickY = ys[0];x1 = 10;y1 = mHeight / 2 + 10;x2 = 10 + mRectWidth;y2 = mHeight / 2 + 10 + mRectHeight;number = "1";} else if (y >= mHeight / 2 + 20 + mRectHeight && y <= mHeight / 2 + 20 + 2 * mRectHeight) { //第二排(4)x1 = 10;y1 = mHeight / 2 + 20 + mRectHeight;x2 = 10 + mRectWidth;y2 = mHeight / 2 + 20 + 2 * mRectHeight;clickY = ys[1];number = "4";} else if (y >= mHeight / 2 + 30 + 2 * mRectHeight && y <= mHeight / 2 + 30 + 3 * mRectHeight) { //第三排(7)x1 = 10;y1 = mHeight / 2 + 30 + 2 * mRectHeight;x2 = 10 + mRectWidth;y2 = mHeight / 2 + 30 + 3 * mRectHeight;clickY = ys[2];number = "7";} else if (y >= mHeight / 2 + 40 + 3 * mRectHeight && y <= mHeight / 2 + 40 + 4 * mRectHeight) { //第四排(0)x1 = 10;y1 = mHeight / 2 + 40 + 3 * mRectHeight;x2 = 10 + mRectWidth;y2 = mHeight / 2 + 40 + 4 * mRectHeight;clickY = ys[3];number = ".";}} else if (x >= 20 + mRectWidth && x <= 20 + 2 * mRectWidth) { //第二列clickX = xs[1];if (y >= mHeight / 2 + 10 && y <= mHeight / 2 + 10 + mRectHeight) { //第一排(2)x1 = 20 + mRectWidth;y1 = mHeight / 2 + 10;x2 = 20 + 2 * mRectWidth;y2 = mHeight / 2 + 10 + mRectHeight;clickY = ys[0];number = "2";} else if (y >= mHeight / 2 + 20 + mRectHeight && y <= mHeight / 2 + 20 + 2 * mRectHeight) { //第二排(5)x1 = 20 + mRectWidth;y1 = mHeight / 2 + 20 + mRectHeight;x2 = 20 + 2 * mRectWidth;y2 = mHeight / 2 + 20 + 2 * mRectHeight;clickY = ys[1];number = "5";} else if (y >= mHeight / 2 + 30 + 2 * mRectHeight && y <= mHeight / 2 + 30 + 3 * mRectHeight) { //第三排(8)x1 = 20 + mRectWidth;y1 = mHeight / 2 + 30 + 2 * mRectHeight;x2 = 20 + 2 * mRectWidth;y2 = mHeight / 2 + 30 + 3 * mRectHeight;clickY = ys[2];number = "8";} else if (y >= mHeight / 2 + 40 + 3 * mRectHeight && y <= mHeight / 2 + 40 + 4 * mRectHeight) { //第四排(0)x1 = 20 + mRectWidth;y1 = mHeight / 2 + 40 + 3 * mRectHeight;x2 = 20 + 2 * mRectWidth;y2 = mHeight / 2 + 40 + 4 * mRectHeight;clickY = ys[3];number = "0";}} else if (x >= 30 + 2 * mRectWidth && x <= 30 + 3 * mRectWidth) { //第三列clickX = xs[2];if (y >= mHeight / 2 + 10 && y <= mHeight / 2 + 10 + mRectHeight) { //第一排(3)x1 = 30 + 2 * mRectWidth;y1 = mHeight / 2 + 10;x2 = 30 + 3 * mRectWidth;y2 = mHeight / 2 + 10 + mRectHeight;clickY = ys[0];number = "3";} else if (y >= mHeight / 2 + 20 + mRectHeight && y <= mHeight / 2 + 20 + 2 * mRectHeight) { //第二排(6)x1 = 30 + 2 * mRectWidth;y1 = mHeight / 2 + 20 + mRectHeight;x2 = 30 + 3 * mRectWidth;y2 = mHeight / 2 + 20 + 2 * mRectHeight;clickY = ys[1];number = "6";} else if (y >= mHeight / 2 + 30 + 2 * mRectHeight && y <= mHeight / 2 + 30 + 3 * mRectHeight) { //第三排(9)x1 = 30 + 2 * mRectWidth;y1 = mHeight / 2 + 30 + 2 * mRectHeight;x2 = 30 + 3 * mRectWidth;y2 = mHeight / 2 + 30 + 3 * mRectHeight;clickY = ys[2];number = "9";} else if (y >= mHeight / 2 + 40 + 3 * mRectHeight && y <= mHeight / 2 + 40 + 4 * mRectHeight) { //第四排(刪除鍵)x1 = 30 + 2 * mRectWidth;y1 = mHeight / 2 + 40 + 3 * mRectHeight;x2 = 30 + 3 * mRectWidth;y2 = mHeight / 2 + 40 + 4 * mRectHeight;clickY = ys[3];number = "delete";}}
}
復制代碼
注:這個方法跟你之前計算出的宮格坐標有關系,所以一定不要計算錯誤
至此,我們寫的差不多了,然后就是要提供一個接口,對外開放,方便用的時候調用,獲取到數字或者其他信息:
public interface OnNumberClickListener {//回調點擊的數字public void onNumberReturn(String number);//刪除鍵的回調public void onNumberDelete();
}
復制代碼
在onTouch事件中使用:
case MotionEvent.ACTION_UP: //彈起invalidate();//刷新界面//一次按下結束,返回點擊的數字if (onNumberClickListener != null) {if (number != null) {if (number.equals("delete")) {onNumberClickListener.onNumberDelete();} else {onNumberClickListener.onNumberReturn(number);}}}//恢復默認setDefault();return true;
復制代碼
然后我們來看一下效果怎么樣吧!
功能也實現了,可是強迫癥很強的我看著很不舒服,不知道你們有沒有,好歹這也是一個鍵盤吧!按下彈起的效果都沒有(沒有改變按下的背景),在這里我們設置一個標志位,按下彈起刷新界面就可以了。在onTouch事件中改變該標識位的值,然后在onDraw方法中判斷該標識位即可:
onTouch方法中增加:
case MotionEvent.ACTION_DOWN: //按下type=0;
case MotionEvent.ACTION_UP: //彈起type=1;
復制代碼
onDraw方法增加:
if (clickX > 0 && clickY > 0) {if (type == 0) { //按下刷新if (number.equals("delete")) {mPaint.setColor(Color.WHITE);canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint);canvas.drawBitmap(mBpDelete, xs[2] - mWidthOfBp / 2 + 10, ys[3] - mHeightOfBp / 2 - 10, mPaint);} else {if (number.equals(".")) {mPaint.setColor(Color.WHITE);} else {mPaint.setColor(Color.GRAY);}canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint);mPaint.setColor(Color.BLACK);mPaint.setTextSize(60);// 設置字體大小mPaint.setStrokeWidth(2);canvas.drawText(number, clickX, clickY, mPaint);}} else if (type == 1) { //抬起刷新if (number.equals("delete")) {mPaint.setColor(Color.GRAY);canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint);canvas.drawBitmap(mBpDelete, xs[2] - mWidthOfBp / 2 + 10, ys[3] - mHeightOfBp / 2 - 10, mPaint);} else {if (number.equals(".")) {mPaint.setColor(Color.GRAY);} else {mPaint.setColor(Color.WHITE);}canvas.drawRoundRect(x1, y1, x2, y2, 10, 10, mPaint);mPaint.setColor(Color.BLACK);mPaint.setTextSize(60);// 設置字體大小mPaint.setStrokeWidth(2);canvas.drawText(number, clickX, clickY, mPaint);}//繪制完成后,重置clickX = 0;clickY = 0;}}
復制代碼
接下來再來看看效果吧:
現在看起來舒服多了,~_~
代碼我已經上傳到Github傳送門,歡迎大家fork,star