android自定义view(自定义数字键盘)
时间:2022-06-06
本文章向大家介绍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
- 你的密码被破解了?看看你的Apple ID、邮箱密码是不是这些!
- Oozie分布式任务的工作流——脚本篇
- hexo配置自己的博客站点
- Oozie分布式任务的工作流——Sqoop篇
- realloc invalid pointer错误解析
- qsort(),sort()排序函数
- 前端打包成桌面应用、以及chrome扩展
- Oozie分布式任务的工作流——Spark篇
- vue 实现 tomato timer(蕃茄钟)
- Linux下TCP连接过程总结
- PHP常用库函数介绍+常见疑难问题解答
- AcFun×讯飞输入法AI方言保护计划 用方言打破次元壁
- directshow、 Emgucv入门
- assert()函数用法
- java教程
- Java快速入门
- Java 开发环境配置
- Java基本语法
- Java 对象和类
- Java 基本数据类型
- Java 变量类型
- Java 修饰符
- Java 运算符
- Java 循环结构
- Java 分支结构
- Java Number类
- Java Character类
- Java String类
- Java StringBuffer和StringBuilder类
- Java 数组
- Java 日期时间
- Java 正则表达式
- Java 方法
- Java 流(Stream)、文件(File)和IO
- Java 异常处理
- Java 继承
- Java 重写(Override)与重载(Overload)
- Java 多态
- Java 抽象类
- Java 封装
- Java 接口
- Java 包(package)
- Java 数据结构
- Java 集合框架
- Java 泛型
- Java 序列化
- Java 网络编程
- Java 发送邮件
- Java 多线程编程
- Java Applet基础
- Java 文档注释
- JavaScript同步、异步及事件循环
- Node.js开发人员都应该知道的12个有用的包
- 欧拉函数的几条重要性质
- 迷宫问题的简单栈实现
- xmuC语言程序实践week 3 大作业
- xmuC语言程序实践week 4 大作业
- R语言预测人口死亡率:用李·卡特(Lee-Carter)模型、非线性模型进行平滑估计
- 前端的发展历程
- R语言蒙特卡洛计算和快速傅立叶变换计算矩生成函数
- Visual Studio 中万能头文件编译不了的解决方案
- Difference in two ways of using lower_bound [C++]std::set::lower_bound与std::lower_bound
- 迷你版Vue--学习如何造一个Vue轮子
- 如何用R语言绘制生成正态分布图表
- hdu 5143 NPY and arithmetic progression(暴力+思维)
- 正则表达式之简易markdown文件解析器