android自定义view(自定义数字键盘)

序言:在上周的项目中,需要做一个密码锁的功能,然后密码下面还得有键盘,就类似支付宝支付的时候那样:

当然了,我们项目的需求简单点,纯数字的就可以了,然后上周就百度了自定义键盘,随便找了一个修改修改就用到项目中去了。

多谢这位简友:[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

原文发布于微信公众号 - Android先生(cyg_24kshign)

原文发表时间:2017-02-12

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏前端知识分享

第4天:JS入门-给div设置宽高背景色

今天学习了js入门课程,听的不多,做了个小练习,给div设置宽高、背景色。一点点都是进步。核心代码如下:

10210
来自专栏前端新视界

使用 canvas 画图时图像文字模糊的解决办法

最近在使用 canvas 画图的时候,遇到了图像文字模糊的问题,解决思路就是根据分辨率绘制不同尺寸的画布。以下是创建高分辨率画布的代码:

11920
来自专栏跟着阿笨一起玩NET

利用vertical-align:middle实现在整个页面居中

如果想让一个div或一张图片相对于整个页面居中,用vertical-align:middle可以很简单地解决。

15210
来自专栏androidBlog

常用的自定义View例子一(流布式布局)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/gdutxiaoxu/article/details/...

7510
来自专栏ytkah

marquee一行代码实现滚动跑马灯效果无需js

  最近ytkah决定拓展一下业务,贴补一些家用,~(@^_^@)~,将以前做的网站建设案例展现出来,有这方面需求的朋友可以扫一下二维码加我哈,或者推荐朋友给我...

96650
来自专栏前端杂货铺

居中详解

 讲解 1,单行文本的居中:           <div class='center'><span>单行文本框居中</span></div>         ...

34990
来自专栏Flutter入门

Android OpenGL ES(八) - 简单实现绿幕抠图

这里的关键是,判断颜色的范围。这里简单的认定 g>140.0 && r<128.0 && b<128.0 时为绿色。当是绿色的时候,就将其颜色换成白色。同时al...

35520
来自专栏企鹅号快讯

《小白HTML5成长之路31》半透明背景的自定义弹窗是怎么用CSS布局的

北方的冬天最怕有风,空气虽然好了,但是异常的冷,吸几口冷风感觉肺都结了冰。一大早小白来到办公室琢磨用户交互方面的问题,看了几个网站发现很多网站的弹窗都是自定义的...

405100
来自专栏从零开始学 Web 前端

从零开始学 Web 之 移动Web(二)JD移动端网页,移动触屏事件

要实现轮播图,必须在首尾添加图片,如果直接在 html 代码直接添加图片的话,由于图片的数量是不固定的,那么每次图片的数量发生改变的话,不仅需要设置 html ...

19310
来自专栏Petrichor的专栏

opencv: 图片 设置 透明度 并 叠加(cv2.addWeighted)

1.1K20

扫码关注云+社区

领取腾讯云代金券