android自定義view(自定義數(shù)字鍵盤(pán))

序言:在上周的項(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

公眾號(hào):Android技術(shù)經(jīng)驗(yàn)分享
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末媚值,一起剝皮案震驚了整個(gè)濱河市狠毯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌褥芒,老刑警劉巖嚼松,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異锰扶,居然都是意外死亡献酗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)坷牛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)罕偎,“玉大人,你說(shuō)我怎么就攤上這事京闰⊙占埃” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵蹂楣,是天一觀的道長(zhǎng)俏站。 經(jīng)常有香客問(wèn)我,道長(zhǎng)痊土,這世上最難降的妖魔是什么肄扎? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮施戴,結(jié)果婚禮上反浓,老公的妹妹穿的比我還像新娘。我一直安慰自己赞哗,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布辆雾。 她就那樣靜靜地躺著肪笋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上藤乙,一...
    開(kāi)封第一講書(shū)人閱讀 52,328評(píng)論 1 310
  • 那天猜揪,我揣著相機(jī)與錄音,去河邊找鬼坛梁。 笑死而姐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的划咐。 我是一名探鬼主播拴念,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼褐缠!你這毒婦竟也來(lái)了政鼠?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤队魏,失蹤者是張志新(化名)和其女友劉穎公般,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體胡桨,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡官帘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昧谊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片遏佣。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖揽浙,靈堂內(nèi)的尸體忽然破棺而出状婶,到底是詐尸還是另有隱情,我是刑警寧澤馅巷,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布膛虫,位于F島的核電站,受9級(jí)特大地震影響钓猬,放射性物質(zhì)發(fā)生泄漏稍刀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一敞曹、第九天 我趴在偏房一處隱蔽的房頂上張望账月。 院中可真熱鬧,春花似錦澳迫、人聲如沸局齿。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)抓歼。三九已至讥此,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間谣妻,已是汗流浹背萄喳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蹋半,地道東北人他巨。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像减江,于是被迫代替她去往敵國(guó)和親染突。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容