仿支付宝支付键盘
来源:互联网 发布:500px哪些摄影师 知乎 编辑:程序博客网 时间:2024/06/10 10:51
第一次拿到这个需求,第一个想法,各种控件嵌套+监听 解决问题。后来想想,这么个东西用这么多控件有点大材小用了,于是就自定义了。
前沿:由于大部分程序员的特性以及工作性质都属于拿来主义者。特此说明,本文章只提供解决思路和关键性代码,不会附带全部代码。
由于只是已Demo方式呈现,并不是一个成熟的自定义控件,好多属性都没有抽离出来,项目写死了。当然也好改。
第一步:构造。这个没什么可说的,在里面初始化一些东西。
public PasswordView(Context context, AttributeSet attrs) { super(context, attrs); init(context, attrs); } private void init(final Context context, AttributeSet attrs) { //初始化画笔相关 int textColor = 0x66666666; int forgetColor = 0xFF0000FF; linePaint = PaintFactory.createAntAndDitherPaint(lineWidth, textColor); //创建一个粗体TextPaint textPaint = PaintFactory.createBoldTextPaint(lineWidth, textColor); textPaint.setTextSize(50); //创建一个忘记密码的Paint forgetPasswordPaint = PaintFactory.createNormalTextPaint(lineWidth, forgetColor); forgetPasswordPaint.setTextSize(30); //输入框 外表 shape相关 inputRoundPaint = PaintFactory.createStrokeRoundPaint(lineWidth, textColor); TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PasswordView); a.recycle(); }
这个不是ViewGroup不需要去排版直接走onMeasure。处于测试阶段,按照作者的手机屏幕来的。实际情况,应当获取到具体值。如果你要问,作者为啥不写。答复:故意的。(不要查水表)。重点就那么几点,知道MeasureSpec的几种常量值,明白他是干嘛的。获取总高度,这里面肯定有坑,而且不少,我建议亲自尝试。我的坑并不一定是你的坑,你的坑我并不一定遇到,就是这样。
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (heightMeasureSpec == MeasureSpec.EXACTLY) { //给width赋值 //尽量用width:match height:wrap } else {// WindowManagerager manager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);// Display display = manager.getDefaultDisplay();// display.getSize(point);// totalWidth = point.x; // TODO: 2017/5/26 测试这么写 ,正式用上面4行代码 totalWidth = 720; keyboardHeight = (totalWidth - lineWidth >> 1) / 3; } //getHeaderTotalHeight()+getKeyboardPreLineHeight()<<2 int measureHeight = (getKeyboardPreLineHeight() << 2) //键盘高度 + getHeaderTotalHeight()//输入支付密码高度 +getPasswordTotalHeight()//密码总高度 +getTextHeight(forgetPasswordPaint)+forgetPasswordMarginBottom;//忘记密码总高度 Log.d("PasswordView", "===width:" + totalWidth + " height:" + measureHeight); setMeasuredDimension(totalWidth, measureHeight); }
那么大头来了,onDraw();这个方法是主要工作内容之一。主要要求熟练掌握 Rect RectF Canvas Paint 的api。Canvas有许多draw方法,如有不熟悉的,自行查询。
分析,功能
这是我们的功能界面。
简单分析一下,有哪些绘制点。
1、有一个标题。
2、密码显示区域。
3、忘记密码。
4、键盘输入界面。
对没有太多自定义控件绘制经验的人来说,drawText可能会比较蛋疼,特别是涉及到偏移量的计算,把握不好。
那么你只需要把你要绘制区域的4点坐标封装在Rect /RectF里然后 测量文字高度很容易得出一个正确的偏移量。
比如:你想绘制 Top 高度 0 Bottom 值 200,想要让文居中。
那么文字居中的计算方式是:
Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt();
top+bottom-fontMetricsInt.bottom-fontMetricsInt.top>>1 的计算结果就是高度的偏移量。
下面就是绘制各种区域:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘制顶部View drawTopValue(canvas); //绘制输入密码框 drawPasswordLine(canvas); //绘制忘记密码 drawForgetPassword(canvas); //绘制键盘 drawKeyboard(canvas); }
其中绘制 键盘逻辑比较重要,我给出我的绘制思路,有更好的欢迎交流
/** * 绘制键盘 */ private KeyboardBean[] keyboards=new KeyboardBean[12];//存放keyboard数组 private Rect keyboardTotalArea;//键盘界面总大小 private void drawKeyboard(Canvas canvas) { int startY=forgetRect.bottom+ forgetPasswordMarginBottom; //绘制4条横线 for (int i=0;i<4;i++) { int horizontalLineStarY=startY+getKeyboardPreLineHeight()*i; canvas.drawLine(0,horizontalLineStarY,totalWidth,horizontalLineStarY+lineWidth,linePaint); } for (int i=0;i<2;i++) { int x_offset =totalWidth/3*(i+1); canvas.drawLine(x_offset,startY,x_offset+1,getHeight(),linePaint); } //将所有keyboard存入数组 if (keyboards[0] == null) { String[] drawValue=new String[]{"1","2","3","4","5","6","7","8","9","","0","<--"}; int preWidth=totalWidth/3; for (int i=0;i<12;i++) { int startX=i%3*preWidth; int endX=startX+preWidth; int y_star=startY+i/3*(keyboardHeight+lineWidth); int y_end=y_star+keyboardHeight+lineWidth; Rect rect=new Rect(startX,y_star,endX,y_end); boolean isFunction=false; if (i == 9 | i == 11) { isFunction=true; } keyboards[i]=new KeyboardBean(isFunction,rect,PaintFactory.createPaint(lineWidth),drawValue[i]); } keyboardTotalArea=new Rect(0,startY,totalWidth,getHeight()); } //去绘制所有背景 for (KeyboardBean b:keyboards) { Rect rect = b.getRect(); canvas.drawRect(rect, b.getBackgroundPaint()); canvas.drawText( b.getDrawText(), rect.centerX()-(measureText(textPaint,b.getDrawText())>>1), getTextOffsetTop(textPaint,rect), textPaint); } } private int measureText(TextPaint paint, String value) { return (int) paint.measureText(value); } /** * 获取文本居中偏移量 */ private int getTextOffsetTop(TextPaint paint,Rect targetRect) { Paint.FontMetricsInt fontMetricsInt = paint.getFontMetricsInt(); return targetRect.bottom+targetRect.top-fontMetricsInt.bottom-fontMetricsInt.top>>1; } /** * 键盘管理实体 */ private class KeyboardBean { //在onDraw的时候会用到是否是按下状态会有不同颜色显示 private boolean isPressed =false; //功能键只有2个 分别为 空白内容 和 删除 private boolean isFunctionKey=false; //记录每一个item的绘制区域 private Rect rect; //画笔 private Paint paint; //绘制文字 private String drawText; //构造 private KeyboardBean( boolean isFunctionKey, Rect rect, Paint paint,String drawText) { this.isFunctionKey = isFunctionKey; this.rect = rect; this.paint = paint; this.drawText=drawText; } //根据不同性质获取不同背景画笔 private Paint getBackgroundPaint() { //按下状态同意设置为这个颜色 if (isPressed) { paint.setStyle(Paint.Style.FILL); paint.setColor(0x6F666666); } else { //功能型按键设置这个颜色 if (isFunctionKey) { paint.setStyle(Paint.Style.FILL); paint.setColor(0x6F999999); } else { //其他建设置为空白 paint.setColor(Color.TRANSPARENT); } } return paint; } //其他一些set和get方法 不再额外添加注释 private Rect getRect() { return rect; } public void setPressed(boolean pressed) { isPressed = pressed; } private String getDrawText() { return drawText; } @Override public String toString() { return "KeyboardBean{" + "isPressed=" + isPressed + ", isFunctionKey=" + isFunctionKey + ", rect=" + rect + ", paint=" + paint + ", drawText='" + drawText + '\'' + '}'; } }
其他的请小伙伴自己根据自己的业务需求去做处理。
其实还有一个重头戏,事件处理。其实不客气的讲,让一个Android开发者自己设计一个OnClickListener的实现,大部分人是实现不了的,有的可能会有一些bug处理不到,当然我的也不一定正确,我没有看过关于onClickListener的实现原理代码,只是凭借我的想法去做这个事情,如果有不同意见,请交流。
先一点一点来。事件主要处理三种 down move 和up
我把这部分所有代码都先放出来
** * 分别是 按下动作 x,y值 移动操作x,y值 抬起动作x,y值 */ float[] touchData=new float[]{0,0,0,0,0,0}; //分别是 按下 移动 抬起 如果没有设置-1 private int defaultIndex=-1; int[] keyboardIndex=new int[]{defaultIndex,defaultIndex,defaultIndex}; @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //按下 touchData[0]=event.getX(); touchData[1]=event.getY(); onDownEvent(); break; case MotionEvent.ACTION_UP: //抬起 touchData[4]=event.getX(); touchData[5]=event.getY(); onUpEvent(); break; case MotionEvent.ACTION_MOVE: //移动 touchData[2]=event.getX(); touchData[3]=event.getY(); onMoveEvent(); break; default: break; } return true; } /** * 按下事件响应 */ private void onDownEvent() { float x=touchData[0]; float y=touchData[1]; //判断区域,并且赋值 action=getAreaFlagByXY(x,y);// if (isKeyboardArea(y)) {//// } else if (isForgetPasswordArea(x, y)) {//// } if (action == ACTION_KEYBOARD) { int index=getIndexByXY(x,y); keyboards[index].setPressed(true); keyboardIndex[0]=index; invalidate(); } } /** * 抬起动作 */ private void onUpEvent() { float x=touchData[4]; float y=touchData[5]; Log.i("up x y","===x:"+x+" y"+y); //判断键盘区域 if (isKeyboardArea(y)&&action==ACTION_KEYBOARD) { int index=getIndexByXY(x,y); //记录抬起下标值 keyboardIndex[2]=index; //按下下标跟抬起下标不相等,置缺省 if (keyboardIndex[0] != keyboardIndex[2]) { keyboardIndex[0] = defaultIndex; } else { //判断是空白还有删除 if (index == 9 || index == 11) { if (index == 11) { //如果是删除就删除 deletePassword(); }//过滤掉是9 没有东西的情况 } else { //如果不是删除就是增加,以下为增加逻辑 if (password.length() < passwordCount) { addPassword(keyboards[index].getDrawText()); } } //设置Key对象为 抬起状态,刷新UI的时候用到 keyboards[index].setPressed(false); //刷新 invalidate(); } } //校验 点击忘记密码区域 if (isForgetPasswordArea(x, y) && action == ACTION_FORGET_PASSWORD) { if (mListener != null) { mListener.onForgetPasswordClick(); } } //置为缺省值 action=ACTION_EMPTY_AREA; } //移动 private void onMoveEvent() { float x=touchData[2]; float y=touchData[3]; if (action == ACTION_KEYBOARD) { KeyboardBean bean= keyboards[keyboardIndex[0]]; if (!bean.getRect().contains((int) x, (int) y)) { keyboards[keyboardIndex[0]].setPressed(false); action=ACTION_EMPTY_AREA; invalidate(); } } }
主要解决问题:抬起和按下的区域不是有效区域,比如 在键盘1按下,在键盘数字4抬起,那么这个触碰事件是无效的。移动过程中移出有效区域,比如按下是键盘数字1 移动到2了 那么此时,1的背景应该不是点击时候的背景,而是默认背景。在抬起动作相应点击事件。
就先写到这里吧。给同学们一个思路,遇到问题可以尝试着换种解决方式,我可以 放心大胆的讲:效率上,这种方式完爆 10几个控件去组合。虽然现在手机好看不出来。。。。。。
- 仿支付宝支付键盘
- 仿支付宝数字支付键盘
- 仿支付宝密码输入键盘
- 仿支付宝输入框+输入键盘
- 仿支付宝密码输入界面+随机键盘
- iOS仿支付宝车牌号码输入键盘
- 分享一下最近写的仿支付宝支付键盘密码输入框demo
- 仿支付宝支付密码输入框
- 仿支付宝支付成功动画
- Android仿支付宝支付验证按钮
- PathMeasure 仿支付宝支付动画
- 仿支付宝支付 密码输入
- android自定义键盘--类似支付宝支付
- 自定义纯数字密码输入键盘-仿支付宝数字密码键盘
- 仿支付宝手势密码
- 仿支付宝数字跳动
- 仿支付宝登录页面
- 仿支付宝账单列表
- Caffe——Python接口学习:绘制loss和accuracy曲线
- 3 常用java虚拟机参数
- 之江学院第0届校赛决赛 A-qwb与支教(容斥+二分)
- np.where函数
- HTTP中GET与POST方法有什么区别
- 仿支付宝支付键盘
- arm函数调用和返回过程详解
- 4 垃圾回收概念与算法
- js实现倒计时
- 374. Guess Number Higher or Lower Easy
- linux常用命令小总结
- gdb调试多线程多进程程序
- Androd.mk
- 5 垃圾收集器与内存分配