android应用九宫格图案锁
2016-03-10 13:46
323 查看
本文源码来源于http://download.csdn.net/download/zhu104/6888127,特此声明。
android应用上的图案锁是一种设立密码的方法,比如支付宝的密码锁,下面我来分析一下其大致的设计过程。
首先,需要自定义一个View,在View中展现密码锁,并设立监听器。
其onDraw函数如下
initCache()是为了设置图案的位置等信息,其代码如下
接下来便是执行drawToCanvas()
如此,九宫格的初始化已经完成,接下来是要设置触摸监听器、处理事件。
reset代码如下
checkSelectPoint()代码如下
clearPassword()代码如下
当sPoints.size() > 0即是已经开始记录点信息,准备核对密码时要画出直线,drawLine()的代码如下
现在到向外提供接口的阶段了。当Activity使用这个View时,需要重写这个接口,以实现该Activity需要实现的任务。代码如下
在九宫格中最重要的自定义View已经差不多讲解完了,大家可以往http://download.csdn.net/download/zhu104/6888127下载完整代码。
android应用上的图案锁是一种设立密码的方法,比如支付宝的密码锁,下面我来分析一下其大致的设计过程。
首先,需要自定义一个View,在View中展现密码锁,并设立监听器。
其onDraw函数如下
@Override public void onDraw(Canvas canvas) { if (!isCache) { initCache(); } drawToCanvas(canvas); }
initCache()是为了设置图案的位置等信息,其代码如下
private void initCache() { //获取View的宽高 w = this.getWidth(); h = this.getHeight(); //x,y为第一个图案的x,y坐标 float x = 0; float y = 0; System.out.println("w:"+w); System.out.println("h:"+h); //根据宽高大小,使图案在View的中间部位。 if (w > h) { x = (w - h) / 2; w = h; } else { y = (h - w) / 2; h = w; } //分别为正常图片,点击后的图片,点击后错误的图片 locus_round_original = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_round_original); locus_round_click = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_round_click); locus_round_click_error = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_round_click_error); //图案与图案之间的线 locus_line = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_line); //图案与图案之间的线的转角部分 locus_line_semicircle = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_line_semicircle); //semicircle半圆形 //错误的线 locus_line_error = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_line_error); //错误的线的转角部分 locus_line_semicircle_error = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_line_semicircle_error); //方向箭头 locus_arrow = BitmapFactory.decodeResource(this.getResources(), R.drawable.locus_arrow); // Log.d("Canvas w h :", "w:" + w + " h:" + h); //canvasMinW的最小宽度 float canvasMinW = w; /*此部分完全可以删去,因为根据以上代码总有w=h if (w > h) { System.out.println("w>h"); canvasMinW = h; } */ //图案的最小宽度 float roundMinW = canvasMinW / 8.0f * 2; //图案的最小半径 float roundW = roundMinW / 2.f; System.out.println("roundMinW:"+roundMinW); System.out.println("roundW:"+roundW); //偏差,这里我还未弄清楚为何要添加偏差 float deviation = canvasMinW % (8 * 2) / 2; System.out.println("canvasMinW % (8 * 2) "+canvasMinW % (8 * 2) ); System.out.println("canvasMinW % (8) "+canvasMinW % (8 ) ); System.out.println("deviation:"+deviation); x += deviation; x += deviation; System.out.println("x:"+x); System.out.println("locus_round_original.getWidth():"+locus_round_original.getWidth()); if (locus_round_original.getWidth() > roundMinW) { //图片缩放比例 float sf = roundMinW * 1.0f / locus_round_original.getWidth(); locus_round_original = BitmapUtil.zoom(locus_round_original, sf); locus_round_click = BitmapUtil.zoom(locus_round_click, sf); locus_round_click_error = BitmapUtil.zoom(locus_round_click_error, sf); locus_line = BitmapUtil.zoom(locus_line, sf); locus_line_semicircle = BitmapUtil.zoom(locus_line_semicircle, sf); locus_line_error = BitmapUtil.zoom(locus_line_error, sf); locus_line_semicircle_error = BitmapUtil.zoom(locus_line_semicircle_error, sf); locus_arrow = BitmapUtil.zoom(locus_arrow, sf); roundW = locus_round_original.getWidth() / 2; } //每张图案的圆点位置 mPoints[0][0] = new Point(x + 0 + roundW, y + 0 + roundW); mPoints[0][1] = new Point(x + w / 2, y + 0 + roundW); mPoints[0][2] = new Point(x + w - roundW, y + 0 + roundW); mPoints[1][0] = new Point(x + 0 + roundW, y + h / 2); mPoints[1][1] = new Point(x + w / 2, y + h / 2); mPoints[1][2] = new Point(x + w - roundW, y + h / 2); mPoints[2][0] = new Point(x + 0 + roundW, y + h - roundW); mPoints[2][1] = new Point(x + w / 2, y + h - roundW); mPoints[2][2] = new Point(x + w - roundW, y + h - roundW); //为每个点设置索引,即密码值,这里注意,二维数组即是数组的数组 int k = 0; for (Point[] ps : mPoints) { for (Point p : ps) { p.index = k; k++; } } //图片的半径 r = locus_round_original.getHeight() / 2; isCache = true; }
接下来便是执行drawToCanvas()
private void drawToCanvas(Canvas canvas) { //根据Point点的状态,来绘制与之符合的图案,初始化时point状态为original for (int i = 0; i < mPoints.length; i++) { for (int j = 0; j < mPoints[i].length; j++) { Point p = mPoints[i][j]; if (p.state == Point.STATE_CHECK) { canvas.drawBitmap(locus_round_click, p.x - r, p.y - r, mPaint); } else if (p.state == Point.STATE_CHECK_ERROR) { canvas.drawBitmap(locus_round_click_error, p.x - r, p.y - r, mPaint); } else { canvas.drawBitmap(locus_round_original, p.x - r, p.y - r, mPaint); } } } //sPoints为已经点中的Point列表,要在这里画出连接的直线,初始化时不用画出直线 if (sPoints.size() > 0) { int tmpAlpha = mPaint.getAlpha(); mPaint.setAlpha(lineAlpha); Point tp = sPoints.get(0); for (int i = 1; i < sPoints.size(); i++) { Point p = sPoints.get(i); drawLine(canvas, tp, p); tp = p; } if (this.movingNoPoint) { drawLine(canvas, tp, new Point((int) moveingX, (int) moveingY)); } mPaint.setAlpha(tmpAlpha); lineAlpha = mPaint.getAlpha(); } }
如此,九宫格的初始化已经完成,接下来是要设置触摸监听器、处理事件。
@Override public boolean onTouchEvent(MotionEvent event) { //是否允许触摸标志 if (!isTouch) { return false; } movingNoPoint = false; float ex = event.getX(); float ey = event.getY(); boolean isFinish = false; boolean redraw = false; Point p = null; switch (event.getAction()) { case MotionEvent.ACTION_DOWN: if (task != null) { task.cancel(); task = null; Log.d("task", "touch cancel()"); } //将之前点击保存在sPoints的全部清空 reset(); //检查是否点击到图案 p = checkSelectPoint(ex, ey); if (p != null) { //点击到图案,允许记录点击的图案信息,准备核对面膜 checking = true; } break; case MotionEvent.ACTION_MOVE: if (checking) { p = checkSelectPoint(ex, ey); if (p == null) { //表示所触摸位置不是图案,手指在移动,准备画线 movingNoPoint = true; moveingX = ex; moveingY = ey; } } break; case MotionEvent.ACTION_UP: p = checkSelectPoint(ex, ey); checking = false; isFinish = true; break; } if (!isFinish && checking && p != null) { //从MotionEvent.ACTION_DOWN:中退出后执行此处 int rk = crossPoint(p); if (rk == 2) //画线 { movingNoPoint = true; moveingX = ex; moveingY = ey; redraw = true; } else if (rk == 0) //所点击的未加入sPoints即首次被点击 { p.state = Point.STATE_CHECK; addPoint(p); redraw = true; } } if (isFinish) { //MotionEvent.ACTION_UP退出后,执行此处 if (this.sPoints.size() == 1) { this.reset(); } else if (this.sPoints.size() < passwordMinLength && this.sPoints.size() > 0) { error();//密码长度不正确,设置sPoints的状态为errpr clearPassword();//先绘制错误显示图,经过800ms后恢复为正常状态 Toast.makeText(this.getContext(), "密码长度不够!", Toast.LENGTH_SHORT).show(); } else if (mCompleteListener != null) { if (this.sPoints.size() >= passwordMinLength) { this.disableTouch(); //监听开始处理得到的密码,如判断是否正确等。 mCompleteListener.onComplete(toPointString()); } } } this.postInvalidate(); return true; }
reset代码如下
private void reset() { for (Point p : sPoints) { p.state = Point.STATE_NORMAL; } sPoints.clear(); this.enableTouch(); }
checkSelectPoint()代码如下
private Point checkSelectPoint(float x, float y) { for (int i = 0; i < mPoints.length; i++) { for (int j = 0; j < mPoints[i].length; j++) { Point p = mPoints[i][j]; if (RoundUtil.checkInRound(p.x, p.y, r, (int) x, (int) y)) { return p; } } } return null; }
public static boolean checkInRound(float sx, float sy, float r, float x, float y) { return Math.sqrt((sx - x) * (sx - x) + (sy - y) * (sy - y)) < r; }checkInRound主要是根据两点的距离来判断。
clearPassword()代码如下
public void clearPassword() { clearPassword(CLEAR_TIME); } public void clearPassword(final long time) { if (time > 1) { if (task != null) { task.cancel(); Log.d("task", "clearPassword cancel()"); } lineAlpha = 130; postInvalidate(); task = new TimerTask() { public void run() { reset(); postInvalidate(); } }; Log.d("task", "clearPassword schedule(" + time + ")"); timer.schedule(task, time); } else { reset(); postInvalidate(); } }
当sPoints.size() > 0即是已经开始记录点信息,准备核对密码时要画出直线,drawLine()的代码如下
private void drawLine(Canvas canvas, Point a, Point b) { //计算两点的距离 float ah = (float) MathUtil.distance(a.x, a.y, b.x, b.y); //获得两个的偏移角度 float degrees = getDegrees(a, b); //canvas旋转 canvas.rotate(degrees, a.x, a.y); if (a.state == Point.STATE_CHECK_ERROR) { mMatrix.setScale((ah - locus_line_semicircle_error.getWidth()) / locus_line_error.getWidth(), 1); mMatrix.postTranslate(a.x, a.y - locus_line_error.getHeight() / 2.0f); canvas.drawBitmap(locus_line_error, mMatrix, mPaint); canvas.drawBitmap(locus_line_semicircle_error, a.x + locus_line_error.getWidth(), a.y - locus_line_error.getHeight() / 2.0f, mPaint); } else { mMatrix.setScale((ah - locus_line_semicircle.getWidth()) / locus_line.getWidth(), 1); mMatrix.postTranslate(a.x, a.y - locus_line.getHeight() / 2.0f); canvas.drawBitmap(locus_line, mMatrix, mPaint); canvas.drawBitmap(locus_line_semicircle, a.x + ah - locus_line_semicircle.getWidth(), a.y - locus_line.getHeight() / 2.0f, mPaint); } canvas.drawBitmap(locus_arrow, a.x, a.y - locus_arrow.getHeight() / 2.0f, mPaint); canvas.rotate(-degrees, a.x, a.y); }
现在到向外提供接口的阶段了。当Activity使用这个View时,需要重写这个接口,以实现该Activity需要实现的任务。代码如下
private OnCompleteListener mCompleteListener; public void setOnCompleteListener(OnCompleteListener mCompleteListener) { this.mCompleteListener = mCompleteListener; } public interface OnCompleteListener { public void onComplete(String password); }
在九宫格中最重要的自定义View已经差不多讲解完了,大家可以往http://download.csdn.net/download/zhu104/6888127下载完整代码。
相关文章推荐
- Android: 启动init.rc 中service的权限问题【转】
- Android init.rc解析【转】
- Android中GridView区中且行间距设置
- android图片的渲染方式
- Android手动打包jar
- Android中跨进程通讯的4种方式
- Android开发之发送短信
- Android与设计模式——观察者(Observer)模式
- Android热补丁动态修复实践
- Android4.4编译出现checkapi错误及解决办法
- Android中 GsonFormat插件解析Jason 数据+和风天气接口解析案例
- Anroid Framework启动过程(android内核剖析笔记)
- 注册谷歌帐号以及用其他镜像解决android sdk的下载问题(已解决)
- android so文件问题
- Android 5.0 SEAndroid下如何获得对一个内核节点的访问权限
- Android 5.0 SEAndroid下如何获得对一个内核节点的访问权限
- Android Studio连接真机没反应
- 在Android开发中使用MVP模式
- Android Volley完全解析(四),带你从源码的角度理解Volley
- Android ListView中加入Button导致ItemClick事件失效