由于换工作换城市,很长时间没有更新了。来到新公司,接到一个需求,要求在PAD屏幕上显示一个数字键盘,作为密码录入。想着练练手,就用自定义View绘制了一个,分享给大家。
效果图:
1.jpg
2.jpg
3.jpg
参考文章:
android自定义view(自定义数字键盘)
实现
首先非常感谢原作者提供的思路,参考了原文后,经过一顿修改和重构,在笔者看来,这篇所绘制的数字键盘,算是原文的进阶版。那么原文的软键盘和笔者的软键盘有什么不同呢?
- 原文的键盘绘制是一个一个按钮绘制的,非常不程序员。
- 由于原文的绘制方法,导致原文的软键盘非常难拓展。例如改变边距、修改键盘内容等等,需要改动大量代码
- 原文的软键盘是固定在屏幕底部的,点击事件也是依赖这个来写的,和我的项目需求有冲突(需求需要显示在屏幕中间,用POP的方式)
主要的实现思路原文已经做了比较细腻的描述,笔者在这里就围绕着上述3项,来讲讲我改动的内容。
首先是数据组装部分
代码语言:javascript复制@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//数据组装
if(coordinates.size()==0) dataInit();//防止作为POP显示时,重复绘制
//开始绘制
drawKeyBoxView(canvas);
}
笔者用了一个Coordinate内部类封装了每一个按钮的位置坐标、内容以及中心点坐标。在绘制前,我们会先进行数据组装。
代码语言:javascript复制private void dataInit(){
for(int i=1;i<10;i ){
int column=(i%3)==0 ? 3 : (i%3);//当前列数
int row=(i-1)==0? 1:(i-1)/3 1;//当前行数
//绘制宫格
Coordinate coordinate=new Coordinate(
left(column), top(row),
right(column), bottom(row), i "",
centerX(column), centetY(row));
coordinates.add(coordinate);
}
//0键
Coordinate coordinate0=new Coordinate(
left(2), top(4),
right(2), bottom(4), "0",
centerX(2), centetY(4));
coordinates.add(coordinate0);
//删除键
Coordinate coordinateDelete=new Coordinate(
left(1), top(4),
right(1), bottom(4), "delete",
centerX(1)-40, centetY(4)-40);
coordinates.add(coordinateDelete);
//确定键
Coordinate coordinateOk=new Coordinate(
left(3), top(4),
right(3), bottom(4), "ok",
centerX(3)-40, centetY(4)-40);
coordinates.add(coordinateOk);
}
/**
* 位置坐标以及中心点坐标运算算法
* @param column
* @return
*/
private float left(int column){
return column*mBaseInterval (column-1)*mRectWidth;
}
笔者找了下规律,写了一个简单算法去获取不同行列键盘按钮的位置坐标以及中心点坐标。这样通过for循环就可以拿到任意个数的Coordinate实体。最下面的3个按键(确定、删除、0)不属于1-n的数字,我们需要单独实例化,不过他们的位置信息,任然是带公式,不需要像原文一样去一个一个绘制。
然后是绘制部分
代码语言:javascript复制 private void drawKeyBoxView(Canvas canvas){
int column=3;//总列数
int row=coordinates.size()/column;//总行数
mPaint.setTextSize(mFontSize);// 设置字体大小
mPaint.setStrokeWidth(2);
mPaint.setTextAlign(Paint.Align.CENTER);
//绘制边框
mPaint.setColor(mBorderColor);
mPaint.setStrokeWidth(mBaseInterval);
for (int i=0;i<=column;i ){
canvas.drawLine(i*(mRectWidth mBaseInterval),0,
i*(mRectWidth mBaseInterval),row*mRectHeight row*mBaseInterval,mPaint);
}
for (int i=0;i<=row;i ){
canvas.drawLine(0,i*(mRectHeight mBaseInterval),
mRectWidth*column column*mBaseInterval,i*(mRectHeight mBaseInterval),mPaint);
}
for (Coordinate coordinate:coordinates){
//画按钮
mPaint.setColor(mButtonColor);
canvas.drawRect(coordinate.getLeft(), coordinate.getTop(),
coordinate.getRight(), coordinate.getBottom(), mPaint);
//画内容
if(coordinate.getValue().equals("ok")){
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.keyboxok),
coordinate.getCenterX(),coordinate.getCenterY(),mPaint);
}else if(coordinate.getValue().equals("delete")){
canvas.drawBitmap(BitmapFactory.decodeResource(getResources(),R.mipmap.keyboxdelete),
coordinate.getCenterX(),coordinate.getCenterY(),mPaint);
}else{
mPaint.setColor(mFontColor);
canvas.drawText(coordinate.getValue(),coordinate.centerX,coordinate.centerY, mPaint);
}
}
}
笔者最开始没有考虑绘制分割线,做的间距留白。就只需要循环我们组装的Coordinate集合绘制就可以了。因为删除键和确定键的内容用的是图片,所以需要单独判断绘制,同时也是因为是图片的原因,间距和绘制文字有差异,绘制图片不能从中心展开绘制,而是从左上角开始绘制的,所以用中心点直接画,不是居中的,需要单独调整。 后面因为设计需求再画上了边框,其实就是围绕着留出的间隙去画线,笔宽就是我们的mBaseInterval(基础间距)
最后我们说说按钮的点击事件
代码语言:javascript复制 @Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_UP:
Log.i(TAG,event.getX() "," event.getY());
handClick(event.getX(),event.getY());
break;
}
return false;
}
/**
* 利用坐标实现点击事件
* @param x
* @param y
*/
public void handClick(float x,float y){
for (Coordinate coordinate:coordinates){
if(coordinate.getLeft()<x&&x<coordinate.getRight()&&
coordinate.getTop()<y&&y<coordinate.getBottom()){
Log.i(TAG,"n您点击了键盘,值为:" coordinate.getValue()
"n起始坐标(" coordinate.getTop() "," coordinate.getTop() ")"
"n结束坐标(" coordinate.getRight() "," coordinate.getBottom() ")");
if(numberKeyBoxViewClick!=null)numberKeyBoxViewClick.click(coordinate.getValue());
else Log.e(TAG,"您没有绑定点击事件,通过实现NumberKeyBoxViewClick接口完成绑定");
return;
}
}
}
原文因为是固定在屏幕下放,点击事件就是根据屏幕宽高算好的。而我的数字键盘可能出现在屏幕的任意位置,自然,原文的方法就不适合我了。 笔者封装的Coordinate实体类中,能够拿到绘制按钮时,左上的角和右下角的坐标,那么很容易就能想到,我们的点击触发区域也就在这区间内。 我们通过Touch事件获取触摸时的手指相对于我们自定义View的坐标,只要我们手指的坐标在左上角和右下角坐标的区间内,即我们可以确认,点击了对应按钮。
总结:
整个绘制下来,其实就是找规律,写公式,画图算坐标的过程。思路清晰后代码其实很简单。笔者因为会在大屏幕中心显示数字键盘,所以没法直接使用系统的软键盘,所以才决定自定义View。 其实用GridView或者其他东西拼接一个九宫格键盘会比自己从头画省很多事,笔者也只是想练练手。而且画出来的更简洁效率更高更易复用以及更自由。当然对于正常键盘操作还是建议直接使用系统自带的,效果更好,更简单。 做了这个东西笔者还顺便写了个密码输入指示器,就是效果图上的圆点。也是个自定义View,不过不是继承View绘制的,而且集成LinearLayout组装的,有兴趣的可以私聊。 希望这篇文章对各位有帮助,并且也希望和大家分享交流。各位去Github下载源码的时候,顺便给笔者的项目库点个红星,谢谢各位了。
NumberKeyBoxView源码地址