Android 图形密码

Android 的九宫格图形密码,废话少说,上源码。

1import java.util.ArrayList; 2import java.util.List; 3import java.util.Timer; 4import java.util.TimerTask; 5 6import android.content.Context; 7import android.graphics.Bitmap; 8import android.graphics.BitmapFactory; 9import android.graphics.Canvas; 10import android.graphics.Matrix; 11import android.graphics.Paint; 12import android.util.AttributeSet; 13import android.util.Log; 14import android.view.MotionEvent; 15import android.view.View; 16import android.widget.Toast; 17import cn.xidian.microfinance.R; 18import cn.xidian.microfinance.app.AppConfig; 19import cn.xidian.microfinance.entity.Point; 20import cn.xidian.microfinance.util.BitmapUtil; 21import cn.xidian.microfinance.util.MD5Util; 22import cn.xidian.microfinance.util.MathUtil; 23import cn.xidian.microfinance.util.RoundUtil; 24import cn.xidian.microfinance.util.StringUtil; 25 26/** 27 * 九宫格解锁 28 */ 29public class LocusPassWordView extends View { 30 private float w = 0; 31 private float h = 0; 32 33 // 34 private boolean isCache = false; 35 // 36 private Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 37 38 // 39 private Point[][] mPoints = new Point[3][3]; 40 // 圆的半径 41 private float r = 0; 42 // 选中的点 43 private List<Point> sPoints = new ArrayList<Point>(); 44 private boolean checking = false; 45 private Bitmap locus_round_original;//圆点初始状态时的图片 46 private Bitmap locus_round_click;//圆点点击时的图片 47 private Bitmap locus_round_click_error;//出错时圆点的图片 48 private Bitmap locus_line;//正常状态下线的图片 49 private Bitmap locus_line_semicircle; 50 private Bitmap locus_line_semicircle_error; 51 private Bitmap locus_arrow;//线的移动方向 52 private Bitmap locus_line_error;//错误状态下的线的图片 53 private long CLEAR_TIME = 0;//清除痕迹的时间 54 private int passwordMinLength = 2;//密码最小长度 55 private boolean isTouch = true; // 是否可操作 56 private Matrix mMatrix = new Matrix(); 57 private int lineAlpha = 50;//连线的透明度 58 59 public LocusPassWordView(Context context, AttributeSet attrs, int defStyle) { 60 super(context, attrs, defStyle); 61 } 62 63 public LocusPassWordView(Context context, AttributeSet attrs) { 64 super(context, attrs); 65 } 66 67 public LocusPassWordView(Context context) { 68 super(context); 69 } 70 71 @Override 72 public void onDraw(Canvas canvas) { 73 if (!isCache) { 74 initCache(); 75 } 76 drawToCanvas(canvas); 77 } 78 79 private void drawToCanvas(Canvas canvas) { 80 81 // mPaint.setColor(Color.RED); 82 // Point p1 = mPoints[1][1]; 83 // Rect r1 = new Rect(p1.x - r,p1.y - r,p1.x + 84 // locus_round_click.getWidth() - r,p1.y+locus_round_click.getHeight()- 85 // r); 86 // canvas.drawRect(r1, mPaint); 87 // 画所有点 88 for (int i = 0; i < mPoints.length; i++) { 89 for (int j = 0; j < mPoints[i].length; j++) { 90 Point p = mPoints[i][j]; 91 if (p.state == Point.STATE_CHECK) { 92 canvas.drawBitmap(locus_round_click, p.x - r, p.y - r, 93 mPaint); 94 } else if (p.state == Point.STATE_CHECK_ERROR) { 95 canvas.drawBitmap(locus_round_click_error, p.x - r, 96 p.y - r, mPaint); 97 } else { 98 canvas.drawBitmap(locus_round_original, p.x - r, p.y - r, 99 mPaint); 100 } 101 } 102 } 103 // mPaint.setColor(Color.BLUE); 104 // canvas.drawLine(r1.left+r1.width()/2, r1.top, r1.left+r1.width()/2, 105 // r1.bottom, mPaint); 106 // canvas.drawLine(r1.left, r1.top+r1.height()/2, r1.right, 107 // r1.bottom-r1.height()/2, mPaint); 108 109 // 画连线 110 if (sPoints.size() > 0) { 111 int tmpAlpha = mPaint.getAlpha(); 112 mPaint.setAlpha(lineAlpha); 113 Point tp = sPoints.get(0); 114 for (int i = 1; i < sPoints.size(); i++) { 115 Point p = sPoints.get(i); 116 drawLine(canvas, tp, p); 117 tp = p; 118 } 119 if (this.movingNoPoint) { 120 drawLine(canvas, tp, new Point((int) moveingX, (int) moveingY)); 121 } 122 mPaint.setAlpha(tmpAlpha); 123 lineAlpha = mPaint.getAlpha(); 124 } 125 126 } 127 128 /** 129 * 初始化Cache信息 130 * 131 * @param canvas 132 */ 133 private void initCache() { 134 135 w = this.getWidth(); 136 h = this.getHeight(); 137 float x = 0; 138 float y = 0; 139 140 // 以最小的为准 141 // 纵屏 142 if (w > h) { 143 x = (w - h) / 2; 144 w = h; 145 } 146 // 横屏 147 else { 148 y = (h - w) / 2; 149 h = w; 150 } 151 152 locus_round_original = BitmapFactory.decodeResource( 153 this.getResources(), R.drawable.locus_round_original); 154 locus_round_click = BitmapFactory.decodeResource(this.getResources(), 155 R.drawable.locus_round_click); 156 locus_round_click_error = BitmapFactory.decodeResource( 157 this.getResources(), R.drawable.locus_round_click_error); 158 159 locus_line = BitmapFactory.decodeResource(this.getResources(), 160 R.drawable.locus_line); 161 locus_line_semicircle = BitmapFactory.decodeResource( 162 this.getResources(), R.drawable.locus_line_semicircle); 163 164 locus_line_error = BitmapFactory.decodeResource(this.getResources(), 165 R.drawable.locus_line_error); 166 locus_line_semicircle_error = BitmapFactory.decodeResource( 167 this.getResources(), R.drawable.locus_line_semicircle_error); 168 169 locus_arrow = BitmapFactory.decodeResource(this.getResources(), 170 R.drawable.locus_arrow); 171 // Log.d("Canvas w h :", "w:" + w + " h:" + h); 172 173 // 计算圆圈图片的大小 174 float canvasMinW = w; 175 if (w > h) { 176 canvasMinW = h; 177 } 178 float roundMinW = canvasMinW / 8.0f * 2; 179 float roundW = roundMinW / 2.f; 180 // 181 float deviation = canvasMinW % (8 * 2) / 2; 182 x += deviation; 183 x += deviation; 184 185 if (locus_round_original.getWidth() > roundMinW) { 186 float sf = roundMinW * 1.0f / locus_round_original.getWidth(); // 取得缩放比例,将所有的图片进行缩放 187 locus_round_original = BitmapUtil.zoom(locus_round_original, sf); 188 locus_round_click = BitmapUtil.zoom(locus_round_click, sf); 189 locus_round_click_error = BitmapUtil.zoom(locus_round_click_error, 190 sf); 191 192 locus_line = BitmapUtil.zoom(locus_line, sf); 193 locus_line_semicircle = BitmapUtil.zoom(locus_line_semicircle, sf); 194 195 locus_line_error = BitmapUtil.zoom(locus_line_error, sf); 196 locus_line_semicircle_error = BitmapUtil.zoom( 197 locus_line_semicircle_error, sf); 198 locus_arrow = BitmapUtil.zoom(locus_arrow, sf); 199 roundW = locus_round_original.getWidth() / 2; 200 } 201 202 mPoints[0][0] = new Point(x + 0 + roundW, y + 0 + roundW); 203 mPoints[0][1] = new Point(x + w / 2, y + 0 + roundW); 204 mPoints[0][2] = new Point(x + w - roundW, y + 0 + roundW); 205 mPoints[1][0] = new Point(x + 0 + roundW, y + h / 2); 206 mPoints[1][1] = new Point(x + w / 2, y + h / 2); 207 mPoints[1][2] = new Point(x + w - roundW, y + h / 2); 208 mPoints[2][0] = new Point(x + 0 + roundW, y + h - roundW); 209 mPoints[2][1] = new Point(x + w / 2, y + h - roundW); 210 mPoints[2][2] = new Point(x + w - roundW, y + h - roundW); 211 int k = 0; 212 for (Point[] ps : mPoints) { 213 for (Point p : ps) { 214 p.index = k; 215 k++; 216 } 217 } 218 r = locus_round_original.getHeight() / 2;// roundW; 219 isCache = true; 220 } 221 222 /** 223 * 画两点的连接 224 * 225 * @param canvas 226 * @param a 227 * @param b 228 */ 229 private void drawLine(Canvas canvas, Point a, Point b) { 230 float ah = (float) MathUtil.distance(a.x, a.y, b.x, b.y); 231 float degrees = getDegrees(a, b); 232 // Log.d("=============x===========", "rotate:" + degrees); 233 canvas.rotate(degrees, a.x, a.y); 234 235 if (a.state == Point.STATE_CHECK_ERROR) { 236 mMatrix.setScale((ah - locus_line_semicircle_error.getWidth()) 237 / locus_line_error.getWidth(), 1); 238 mMatrix.postTranslate(a.x, a.y - locus_line_error.getHeight() 239 / 2.0f); 240 canvas.drawBitmap(locus_line_error, mMatrix, mPaint); 241 canvas.drawBitmap(locus_line_semicircle_error, a.x 242 + locus_line_error.getWidth(), 243 a.y - locus_line_error.getHeight() / 2.0f, mPaint); 244 } else { 245 mMatrix.setScale((ah - locus_line_semicircle.getWidth()) 246 / locus_line.getWidth(), 1); 247 mMatrix.postTranslate(a.x, a.y - locus_line.getHeight() / 2.0f); 248 canvas.drawBitmap(locus_line, mMatrix, mPaint); 249 canvas.drawBitmap(locus_line_semicircle, a.x + ah 250 - locus_line_semicircle.getWidth(), 251 a.y - locus_line.getHeight() / 2.0f, mPaint); 252 } 253 254 canvas.drawBitmap(locus_arrow, a.x, a.y - locus_arrow.getHeight() 255 / 2.0f, mPaint); 256 257 canvas.rotate(-degrees, a.x, a.y); 258 259 } 260 261 public float getDegrees(Point a, Point b) { 262 float ax = a.x;// a.index % 3; 263 float ay = a.y;// a.index / 3; 264 float bx = b.x;// b.index % 3; 265 float by = b.y;// b.index / 3; 266 float degrees = 0; 267 if (bx == ax) // y轴相等 90度或270 268 { 269 if (by > ay) // 在y轴的下边 90 270 { 271 degrees = 90; 272 } else if (by < ay) // 在y轴的上边 270 273 { 274 degrees = 270; 275 } 276 } else if (by == ay) // y轴相等 0度或180 277 { 278 if (bx > ax) // 在y轴的下边 90 279 { 280 degrees = 0; 281 } else if (bx < ax) // 在y轴的上边 270 282 { 283 degrees = 180; 284 } 285 } else { 286 if (bx > ax) // 在y轴的右边 270~90 287 { 288 if (by > ay) // 在y轴的下边 0 - 90 289 { 290 degrees = 0; 291 degrees = degrees 292 + switchDegrees(Math.abs(by - ay), 293 Math.abs(bx - ax)); 294 } else if (by < ay) // 在y轴的上边 270~0 295 { 296 degrees = 360; 297 degrees = degrees 298 - switchDegrees(Math.abs(by - ay), 299 Math.abs(bx - ax)); 300 } 301 302 } else if (bx < ax) // 在y轴的左边 90~270 303 { 304 if (by > ay) // 在y轴的下边 180 ~ 270 305 { 306 degrees = 90; 307 degrees = degrees 308 + switchDegrees(Math.abs(bx - ax), 309 Math.abs(by - ay)); 310 } else if (by < ay) // 在y轴的上边 90 ~ 180 311 { 312 degrees = 270; 313 degrees = degrees 314 - switchDegrees(Math.abs(bx - ax), 315 Math.abs(by - ay)); 316 } 317 318 } 319 320 } 321 return degrees; 322 } 323 324 /** 325 * 1=30度 2=45度 4=60度 326 * 327 * @param tan 328 * @return 329 */ 330 private float switchDegrees(float x, float y) { 331 return (float) MathUtil.pointTotoDegrees(x, y); 332 } 333 334 /** 335 * 取得数组下标 336 * 337 * @param index 338 * @return 339 */ 340 public int[] getArrayIndex(int index) { 341 int[] ai = new int[2]; 342 ai[0] = index / 3; 343 ai[1] = index % 3; 344 return ai; 345 } 346 347 /** 348 * 349 * 检查 350 * 351 * @param x 352 * @param y 353 * @return 354 */ 355 private Point checkSelectPoint(float x, float y) { 356 for (int i = 0; i < mPoints.length; i++) { 357 for (int j = 0; j < mPoints[i].length; j++) { 358 Point p = mPoints[i][j]; 359 if (RoundUtil.checkInRound(p.x, p.y, r, (int) x, (int) y)) { 360 return p; 361 } 362 } 363 } 364 return null; 365 } 366 367 /** 368 * 重置 369 */ 370 private void reset() { 371 for (Point p : sPoints) { 372 p.state = Point.STATE_NORMAL; 373 } 374 sPoints.clear(); 375 this.enableTouch(); 376 } 377 378 /** 379 * 判断点是否有交叉 返回 0,新点 ,1 与上一点重叠 2,与非最后一点重叠 380 * 381 * @param p 382 * @return 383 */ 384 private int crossPoint(Point p) { 385 // 重叠的不最后一个则 reset 386 if (sPoints.contains(p)) { 387 if (sPoints.size() > 2) { 388 // 与非最后一点重叠 389 if (sPoints.get(sPoints.size() - 1).index != p.index) { 390 return 2; 391 } 392 } 393 return 1; // 与最后一点重叠 394 } else { 395 return 0; // 新点 396 } 397 } 398 399 /** 400 * 添加一个点 401 * 402 * @param point 403 */ 404 private void addPoint(Point point) { 405 this.sPoints.add(point); 406 } 407 408 /** 409 * 转换为String 410 * 411 * @param points 412 * @return 413 */ 414 private String toPointString() { 415 if (sPoints.size() > passwordMinLength) { 416 StringBuffer sf = new StringBuffer(); 417 for (Point p : sPoints) { 418 sf.append(","); 419 sf.append(p.index); 420 } 421 return sf.deleteCharAt(0).toString(); 422 } else { 423 return ""; 424 } 425 } 426 427 boolean movingNoPoint = false; 428 float moveingX, moveingY; 429 430 @Override 431 public boolean onTouchEvent(MotionEvent event) { 432 // 不可操作 433 if (!isTouch) { 434 return false; 435 } 436 437 movingNoPoint = false; 438 439 float ex = event.getX(); 440 float ey = event.getY(); 441 boolean isFinish = false; 442 boolean redraw = false; 443 Point p = null; 444 switch (event.getAction()) { 445 case MotionEvent.ACTION_DOWN: // 点下 446 // 如果正在清除密码,则取消 447 if (task != null) { 448 task.cancel(); 449 task = null; 450 Log.d("task", "touch cancel()"); 451 } 452 // 删除之前的点 453 reset(); 454 p = checkSelectPoint(ex, ey); 455 if (p != null) { 456 checking = true; 457 } 458 break; 459 case MotionEvent.ACTION_MOVE: // 移动 460 if (checking) { 461 p = checkSelectPoint(ex, ey); 462 if (p == null) { 463 movingNoPoint = true; 464 moveingX = ex; 465 moveingY = ey; 466 } 467 } 468 break; 469 case MotionEvent.ACTION_UP: // 提起 470 p = checkSelectPoint(ex, ey); 471 checking = false; 472 isFinish = true; 473 break; 474 } 475 if (!isFinish && checking && p != null) { 476 477 int rk = crossPoint(p); 478 if (rk == 2) // 与非最后一重叠 479 { 480 // reset(); 481 // checking = false; 482 483 movingNoPoint = true; 484 moveingX = ex; 485 moveingY = ey; 486 487 redraw = true; 488 } else if (rk == 0) // 一个新点 489 { 490 p.state = Point.STATE_CHECK; 491 addPoint(p); 492 redraw = true; 493 } 494 // rk == 1 不处理 495 496 } 497 498 // 是否重画 499 if (redraw) { 500 501 } 502 if (isFinish) { 503 if (this.sPoints.size() == 1) { 504 this.reset(); 505 } else if (this.sPoints.size() < passwordMinLength 506 && this.sPoints.size() > 0) { 507 // mCompleteListener.onPasswordTooMin(sPoints.size()); 508 error(); 509 clearPassword(); 510 Toast.makeText(this.getContext(), "密码太短,请重新输入!", 511 Toast.LENGTH_SHORT).show(); 512 } else if (mCompleteListener != null) { 513 if (this.sPoints.size() >= passwordMinLength) { 514 this.disableTouch(); 515 mCompleteListener.onComplete(toPointString()); 516 } 517 518 } 519 } 520 this.postInvalidate(); 521 return true; 522 } 523 524 /** 525 * 设置已经选中的为错误 526 */ 527 private void error() { 528 for (Point p : sPoints) { 529 p.state = Point.STATE_CHECK_ERROR; 530 } 531 } 532 533 /** 534 * 设置为输入错误 535 */ 536 public void markError() { 537 markError(CLEAR_TIME); 538 } 539 540 /** 541 * 设置为输入错误 542 */ 543 public void markError(final long time) { 544 for (Point p : sPoints) { 545 p.state = Point.STATE_CHECK_ERROR; 546 } 547 this.clearPassword(time); 548 } 549 550 /** 551 * 设置为可操作 552 */ 553 public void enableTouch() { 554 isTouch = true; 555 } 556 557 /** 558 * 设置为不可操作 559 */ 560 public void disableTouch() { 561 isTouch = false; 562 } 563 564 private Timer timer = new Timer(); 565 private TimerTask task = null; 566 567 /** 568 * 清除密码 569 */ 570 public void clearPassword() { 571 clearPassword(CLEAR_TIME); 572 } 573 574 /** 575 * 清除密码 576 */ 577 public void clearPassword(final long time) { 578 if (time > 1) { 579 if (task != null) { 580 task.cancel(); 581 Log.d("task", "clearPassword cancel()"); 582 } 583 lineAlpha = 130; 584 postInvalidate(); 585 task = new TimerTask() { 586 public void run() { 587 reset(); 588 postInvalidate(); 589 } 590 }; 591 Log.d("task", "clearPassword schedule(" + time + ")"); 592 timer.schedule(task, time); 593 } else { 594 reset(); 595 postInvalidate(); 596 } 597 598 } 599 600 // 601 private OnCompleteListener mCompleteListener; 602 603 /** 604 * @param mCompleteListener 605 */ 606 public void setOnCompleteListener(OnCompleteListener mCompleteListener) { 607 this.mCompleteListener = mCompleteListener; 608 } 609 610 /** 611 * 取得密码 612 * 613 * @return 614 */ 615 private String getPassword(Context context) { 616// SharedPreferences settings = this.getContext().getSharedPreferences( 617// this.getClass().getName(), 0); 618// return settings.getString("password", ""); // , "0,1,2,3,4,5,6,7,8" 619 return AppConfig.getGraphPassword(context); 620 } 621 622 623 public boolean verifyPassword(Context context, String password) { 624 boolean verify = false; 625 if (StringUtil.isNotEmpty(password)) { 626 password = MD5Util.MD5(password); 627 if (password.equals(getPassword(context))) { 628 verify = true; 629 } 630 } 631 return verify; 632 } 633 634 /** 635 * 设置密码 636 * 637 * @param password 638 */ 639 public void setPassWord(Context context, String password) { 640 password = MD5Util.MD5(password); 641 AppConfig.setGraphPassword(context, password); 642 } 643 644 public int getPasswordMinLength() { 645 return passwordMinLength; 646 } 647 648 public void setPasswordMinLength(int passwordMinLength) { 649 this.passwordMinLength = passwordMinLength; 650 } 651 652 /** 653 * 轨迹球画完成事件 654 * 655 * @author way 656 */ 657 public interface OnCompleteListener { 658 /** 659 * 画完了 660 * 661 * @param str 662 */ 663 public void onComplete(String mpassword); 664 } 665}

资源文件: http://pan.baidu.com/s/1qu4Og

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Service starting has been prevented by iaware or trustsbase sInfo ServiceInfo 解决方法

问题:ActivityManager:ServicestartinghasbeenpreventedbyiawareortrustsbasesInfoServiceInfo{c50ea35xxx.xxx.xxx.ServiceName}问题描述,该问题再华为部分手机升级到Android10.1之后,启动服务会

Android So动态加载 优雅实现与原理分析

背景:漫品Android客户端集成适配转换功能(基于目标识别(So库35M)和人脸识别库(5M)),导致apk体积50M左右,为优化客户端体验,决定实现So文件动态加载.!(https://oscimg.oschina.net/oscnet/00d1ff90e4b34869664fef59e3ec3fdd20b.png)点击上方“蓝字”关注我

mysql用户

1\.学会能按着需求创建一个帐号2\.知道连接字符串是什么样3\.密码密码怎么恢复mysql用户权限介绍mysql用户管理 !(https://oscimg.oschina.net/oscnet/368d3c1e00a0a9515545c2962660a27a080.png)!(https://oscimg.oschin