序言:在上周的項(xiàng)目中豺旬,需要做一個(gè)密碼鎖的功能钠惩,然后密碼下面還得有鍵盤(pán),就類(lèi)似支付寶支付的時(shí)候那樣:
當(dāng)然了族阅,我們項(xiàng)目的需求簡(jiǎn)單點(diǎn)篓跛,純數(shù)字的就可以了,然后上周就百度了自定義鍵盤(pán)坦刀,隨便找了一個(gè)修改修改就用到項(xiàng)目中去了愧沟。
多謝這位簡(jiǎn)友:[Android] 自定義輸入支付密碼的軟鍵盤(pán)
今天自己抽空寫(xiě)了一個(gè)自定義View的鍵盤(pán)控件,下面跟大家分享一下:
思路:
1求泰、布局:
(1)央渣、宮格:我們可以將這個(gè)布局看成是宮格布局计盒,然后需要計(jì)算出每個(gè)小宮格在屏幕中的位置(坐標(biāo))渴频,然后再用canvas畫(huà)出相應(yīng)的矩形即可。
(2)北启、數(shù)字:我們需要計(jì)算出每個(gè)小宮格中的中心點(diǎn)位置(坐標(biāo))卜朗,然后用canvas畫(huà)數(shù)字上去,當(dāng)然咕村,我們知道最后一個(gè)是刪除鍵场钉,并不是數(shù)字,我們可以準(zhǔn)備一張圖片懈涛,將圖片畫(huà)到相應(yīng)的位置逛万。
2、用戶(hù)動(dòng)作:
(1)批钠、按下:用戶(hù)每一次按下的時(shí)候就表示這一次動(dòng)作的開(kāi)始宇植,所以首先要將各種標(biāo)識(shí)位(自定義所需要的標(biāo)識(shí)位)設(shè)置成初始狀態(tài)得封,然后需要記錄按下的坐標(biāo),然后計(jì)算出用戶(hù)按下的坐標(biāo)與宮格中哪個(gè)點(diǎn)相對(duì)應(yīng)指郁,在記錄相應(yīng)數(shù)字忙上。最后刷新布局
(2)、抬起:用戶(hù)抬起的時(shí)候需要刷新布局闲坎,然后將按下過(guò)程中記錄下的數(shù)字或者刪除鍵信息通過(guò)接口的形式回調(diào)給用戶(hù)疫粥,最后恢復(fù)標(biāo)識(shí)位
(3)、取消:將所有的標(biāo)識(shí)位恢復(fù)成初始狀態(tài)腰懂。
好了梗逮,思路就講到這里,我們來(lái)看看onDraw方法:
protected void onDraw(Canvas canvas) {
if (!isInit) {
initData();
}
mPaint.setColor(Color.WHITE);
//畫(huà)宮格
//第一排
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);// 設(shè)置字體大小
mPaint.setStrokeWidth(2);
//畫(huà)數(shù)字
//第一排
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);
}
注:上面的坐標(biāo)需要我們自己算出绣溜,耐心一點(diǎn)库糠,很容易算的,你只需要搞清楚在Android中屏幕是怎樣的坐標(biāo)系就可以了涮毫。坐標(biāo)算出來(lái)之后瞬欧,我們先畫(huà)宮格,然后再畫(huà)數(shù)字和刪除鍵罢防,這里有人要問(wèn)了艘虎,我可以先畫(huà)數(shù)字嗎,NO咒吐,因?yàn)楫?dāng)你畫(huà)完數(shù)字之后再畫(huà)宮格你會(huì)發(fā)現(xiàn)數(shù)字不見(jiàn)了野建,為什么呢?被你的宮格擋住了 >_<所以千萬(wàn)別這樣做恬叹。畫(huà)的過(guò)程中別忘了將畫(huà)筆設(shè)置一些你需要的屬性候生。
好了,畫(huà)好之后绽昼,我們來(lái)看看效果怎么樣:
樣式出來(lái)了哈唯鸭!但是設(shè)計(jì)的沒(méi)那么好看,大家將就看一看哈>_<
然后我們需要重寫(xiě)onTouch事件硅确,在里面判斷用戶(hù)的一些行為:
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: //按下
//恢復(fù)默認(rèn)值
setDefault();
/**
*判斷按下的點(diǎn)在哪個(gè)宮格中
*/
invalidate();//刷新界面
return true;
case MotionEvent.ACTION_UP: //彈起
invalidate();//刷新界面
/**
*一次按下結(jié)束,返回點(diǎn)擊的數(shù)字
*/
//恢復(fù)默認(rèn)
setDefault();
return true;
case MotionEvent.ACTION_CANCEL: //取消
//恢復(fù)默認(rèn)值
setDefault();
return true;
}
如上面?zhèn)未a所示目溉,我寫(xiě)了一個(gè)方法來(lái)判斷按下的點(diǎn)在哪個(gè)宮格中:
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";
}
}
}
注:這個(gè)方法跟你之前計(jì)算出的宮格坐標(biāo)有關(guān)系,所以一定不要計(jì)算錯(cuò)誤
至此菱农,我們寫(xiě)的差不多了缭付,然后就是要提供一個(gè)接口,對(duì)外開(kāi)放循未,方便用的時(shí)候調(diào)用陷猫,獲取到數(shù)字或者其他信息:
public interface OnNumberClickListener {
//回調(diào)點(diǎn)擊的數(shù)字
public void onNumberReturn(String number);
//刪除鍵的回調(diào)
public void onNumberDelete();
}
在onTouch事件中使用:
case MotionEvent.ACTION_UP: //彈起
invalidate();//刷新界面
//一次按下結(jié)束,返回點(diǎn)擊的數(shù)字
if (onNumberClickListener != null) {
if (number != null) {
if (number.equals("delete")) {
onNumberClickListener.onNumberDelete();
} else {
onNumberClickListener.onNumberReturn(number);
}
}
}
//恢復(fù)默認(rèn)
setDefault();
return true;
然后我們來(lái)看一下效果怎么樣吧!
功能也實(shí)現(xiàn)了,可是強(qiáng)迫癥很強(qiáng)的我看著很不舒服绣檬,不知道你們有沒(méi)有舅巷,好歹這也是一個(gè)鍵盤(pán)吧!按下彈起的效果都沒(méi)有(沒(méi)有改變按下的背景)河咽,在這里我們?cè)O(shè)置一個(gè)標(biāo)志位钠右,按下彈起刷新界面就可以了。在onTouch事件中改變?cè)摌?biāo)識(shí)位的值忘蟹,然后在onDraw方法中判斷該標(biāo)識(shí)位即可:
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);// 設(shè)置字體大小
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);// 設(shè)置字體大小
mPaint.setStrokeWidth(2);
canvas.drawText(number, clickX, clickY, mPaint);
}
//繪制完成后,重置
clickX = 0;
clickY = 0;
}
}
接下來(lái)再來(lái)看看效果吧:
現(xiàn)在看起來(lái)舒服多了飒房,_
代碼我已經(jīng)上傳到Github傳送門(mén),歡迎大家fork,star