http://blog.csdn.net/way_ping_li/article/details/17015153

http://blog.csdn.net/way_ping_li/article/details/17015153

http://blog.csdn.net/way_ping_li/article/details/17015153

http://blog.csdn.net/way_ping_li/article/details/17015153

http://blog.csdn.net/way_ping_li/article/details/17015153


Android之高仿手机QQ图案解锁

分类:Android 4757人阅读 评论(19) 收藏 举报

本文源码(utf-8编码):http://download.csdn.net/detail/weidi1989/6628211

ps:请不要再问我,为什么导入之后会乱码了。

其实,代码基本上都是从原生系统中提取的:LockPatternView、加密工具类,以及解锁逻辑等,我只是稍作修改,大家都知道,原生系统界面比较丑陋,因此,我特意把QQ的apk解压了,从中拿了几张图案解锁的图片,一个简单的例子就这样诞生了。

好了,废话不多说,我们来看看效果(最后两张是最新4.4系统,炫一下,呵呵):

Android之高仿手机QQ图案解锁_第1张图片Android之高仿手机QQ图案解锁_第2张图片Android之高仿手机QQ图案解锁_第3张图片Android之高仿手机QQ图案解锁_第4张图片Android之高仿手机QQ图案解锁_第5张图片Android之高仿手机QQ图案解锁_第6张图片Android之高仿手机QQ图案解锁_第7张图片


1.最关健的就是那个自定义九宫格View,代码来自framework下:LockPatternView,原生系统用的图片资源比较多,好像有7、8张吧,而且绘制的比较复杂,我找寻半天,眼睛都找瞎了,发现解压的QQ里面就3张图片,一个圈圈,两个点,没办法,只能修改代码了,在修改的过程中,才发现,其实可以把原生的LockPatternView给简化,绘制更少的图片,达到更好的效果。总共优化有:①去掉了连线的箭头,②原生的连线只有白色一种,改成根据不同状态显示黄色和红色两张色,③.原生view是先画点再画线,使得线覆盖在点的上面,影响美观,改成先画连线再画点。

关健部分代码onDraw函数:

[java] view plain copy
  1. @Override
  2. protectedvoidonDraw(Canvascanvas){
  3. finalArrayList<Cell>pattern=mPattern;
  4. finalintcount=pattern.size();
  5. finalboolean[][]drawLookup=mPatternDrawLookup;
  6. if(mPatternDisplayMode==DisplayMode.Animate){
  7. //figureoutwhichcirclestodraw
  8. //+1sowepauseoncompletepattern
  9. finalintoneCycle=(count+1)*MILLIS_PER_CIRCLE_ANIMATING;
  10. finalintspotInCycle=(int)(SystemClock.elapsedRealtime()-mAnimatingPeriodStart)
  11. %oneCycle;
  12. finalintnumCircles=spotInCycle/MILLIS_PER_CIRCLE_ANIMATING;
  13. clearPatternDrawLookup();
  14. for(inti=0;i<numCircles;i++){
  15. finalCellcell=pattern.get(i);
  16. drawLookup[cell.getRow()][cell.getColumn()]=true;
  17. }
  18. //figureoutinprogressportionofghostingline
  19. finalbooleanneedToUpdateInProgressPoint=numCircles>0
  20. &&numCircles<count;
  21. if(needToUpdateInProgressPoint){
  22. finalfloatpercentageOfNextCircle=((float)(spotInCycle%MILLIS_PER_CIRCLE_ANIMATING))
  23. /MILLIS_PER_CIRCLE_ANIMATING;
  24. finalCellcurrentCell=pattern.get(numCircles-1);
  25. finalfloatcenterX=getCenterXForColumn(currentCell.column);
  26. finalfloatcenterY=getCenterYForRow(currentCell.row);
  27. finalCellnextCell=pattern.get(numCircles);
  28. finalfloatdx=percentageOfNextCircle
  29. *(getCenterXForColumn(nextCell.column)-centerX);
  30. finalfloatdy=percentageOfNextCircle
  31. *(getCenterYForRow(nextCell.row)-centerY);
  32. mInProgressX=centerX+dx;
  33. mInProgressY=centerY+dy;
  34. }
  35. //TODO:Infiniteloophere...
  36. invalidate();
  37. }
  38. finalfloatsquareWidth=mSquareWidth;
  39. finalfloatsquareHeight=mSquareHeight;
  40. floatradius=(squareWidth*mDiameterFactor*0.5f);
  41. mPathPaint.setStrokeWidth(radius);
  42. finalPathcurrentPath=mCurrentPath;
  43. currentPath.rewind();
  44. //TODO:thepathshouldbecreatedandcachedeverytimewehit-detect
  45. //acell
  46. //onlythelastsegmentofthepathshouldbecomputedhere
  47. //drawthepathofthepattern(unlesstheuserisinprogress,and
  48. //weareinstealthmode)
  49. finalbooleandrawPath=(!mInStealthMode||mPatternDisplayMode==DisplayMode.Wrong);
  50. //drawthearrowsassociatedwiththepath(unlesstheuserisin
  51. //progress,and
  52. //weareinstealthmode)
  53. booleanoldFlag=(mPaint.getFlags()&Paint.FILTER_BITMAP_FLAG)!=0;
  54. mPaint.setFilterBitmap(true);//drawwithhigherqualitysincewe
  55. //renderwithtransforms
  56. //drawthelines
  57. if(drawPath){
  58. booleananyCircles=false;
  59. for(inti=0;i<count;i++){
  60. Cellcell=pattern.get(i);
  61. //onlydrawthepartofthepatternstoredin
  62. //thelookuptable(thisisonlydifferentinthecase
  63. //ofanimation).
  64. if(!drawLookup[cell.row][cell.column]){
  65. break;
  66. }
  67. anyCircles=true;
  68. floatcenterX=getCenterXForColumn(cell.column);
  69. floatcenterY=getCenterYForRow(cell.row);
  70. if(i==0){
  71. currentPath.moveTo(centerX,centerY);
  72. }else{
  73. currentPath.lineTo(centerX,centerY);
  74. }
  75. }
  76. //addlastinprogresssection
  77. if((mPatternInProgress||mPatternDisplayMode==DisplayMode.Animate)
  78. &&anyCircles){
  79. currentPath.lineTo(mInProgressX,mInProgressY);
  80. }
  81. //changthelinecolorindifferentDisplayMode
  82. if(mPatternDisplayMode==DisplayMode.Wrong)
  83. mPathPaint.setColor(Color.RED);
  84. else
  85. mPathPaint.setColor(Color.YELLOW);
  86. canvas.drawPath(currentPath,mPathPaint);
  87. }
  88. //drawthecircles
  89. finalintpaddingTop=getPaddingTop();
  90. finalintpaddingLeft=getPaddingLeft();
  91. for(inti=0;i<3;i++){
  92. floattopY=paddingTop+i*squareHeight;
  93. //floatcenterY=mPaddingTop+i*mSquareHeight+(mSquareHeight
  94. ///2);
  95. for(intj=0;j<3;j++){
  96. floatleftX=paddingLeft+j*squareWidth;
  97. drawCircle(canvas,(int)leftX,(int)topY,drawLookup[i][j]);
  98. }
  99. }
  100. mPaint.setFilterBitmap(oldFlag);//restoredefaultflag
  101. }

2.第二个值得学习的地方是(代码来自设置应用中):在创建解锁图案时的枚举使用,原生代码中使用了很多枚举,将绘制图案时的状态、底部两个按钮状态、顶部一个TextView显示的提示文字都紧密的联系起来。因此,只用监听LockPatternView动态变化,对应改变底部Button和顶部TextView的状态即可实现联动,简单的方法可以实现很多代码才能实现的逻辑,个人很喜欢。

①全局的状态:

[java] view plain copy
  1. /**
  2. *Keeptrackinternallyofwheretheuserisinchoosingapattern.
  3. */
  4. protectedenumStage{
  5. //初始状态
  6. Introduction(R.string.lockpattern_recording_intro_header,
  7. LeftButtonMode.Cancel,RightButtonMode.ContinueDisabled,
  8. ID_EMPTY_MESSAGE,true),
  9. //帮助状态
  10. HelpScreen(R.string.lockpattern_settings_help_how_to_record,
  11. LeftButtonMode.Gone,RightButtonMode.Ok,ID_EMPTY_MESSAGE,
  12. false),
  13. //绘制过短
  14. ChoiceTooShort(R.string.lockpattern_recording_incorrect_too_short,
  15. LeftButtonMode.Retry,RightButtonMode.ContinueDisabled,
  16. ID_EMPTY_MESSAGE,true),
  17. //第一次绘制图案
  18. FirstChoiceValid(R.string.lockpattern_pattern_entered_header,
  19. LeftButtonMode.Retry,RightButtonMode.Continue,
  20. ID_EMPTY_MESSAGE,false),
  21. //需要再次绘制确认
  22. NeedToConfirm(R.string.lockpattern_need_to_confirm,
  23. LeftButtonMode.Cancel,RightButtonMode.ConfirmDisabled,
  24. ID_EMPTY_MESSAGE,true),
  25. //确认出错
  26. ConfirmWrong(R.string.lockpattern_need_to_unlock_wrong,
  27. LeftButtonMode.Cancel,RightButtonMode.ConfirmDisabled,
  28. ID_EMPTY_MESSAGE,true),
  29. //选择确认
  30. ChoiceConfirmed(R.string.lockpattern_pattern_confirmed_header,
  31. LeftButtonMode.Cancel,RightButtonMode.Confirm,
  32. ID_EMPTY_MESSAGE,false);
  33. /**
  34. *@paramheaderMessage
  35. *Themessagedisplayedatthetop.
  36. *@paramleftMode
  37. *Themodeoftheleftbutton.
  38. *@paramrightMode
  39. *Themodeoftherightbutton.
  40. *@paramfooterMessage
  41. *Thefootermessage.
  42. *@parampatternEnabled
  43. *Whetherthepatternwidgetisenabled.
  44. */
  45. Stage(intheaderMessage,LeftButtonModeleftMode,
  46. RightButtonModerightMode,intfooterMessage,
  47. booleanpatternEnabled){
  48. this.headerMessage=headerMessage;
  49. this.leftMode=leftMode;
  50. this.rightMode=rightMode;
  51. this.footerMessage=footerMessage;
  52. this.patternEnabled=patternEnabled;
  53. }
  54. finalintheaderMessage;
  55. finalLeftButtonModeleftMode;
  56. finalRightButtonModerightMode;
  57. finalintfooterMessage;
  58. finalbooleanpatternEnabled;
  59. }

②.底部两个按钮的状态枚举:

[java] view plain copy
  1. /**
  2. *Thestatesoftheleftfooterbutton.
  3. */
  4. enumLeftButtonMode{
  5. //取消
  6. Cancel(android.R.string.cancel,true),
  7. //取消时禁用
  8. CancelDisabled(android.R.string.cancel,false),
  9. //重试
  10. Retry(R.string.lockpattern_retry_button_text,true),
  11. //重试时禁用
  12. RetryDisabled(R.string.lockpattern_retry_button_text,false),
  13. //消失
  14. Gone(ID_EMPTY_MESSAGE,false);
  15. /**
  16. *@paramtext
  17. *Thedisplayedtextforthismode.
  18. *@paramenabled
  19. *Whetherthebuttonshouldbeenabled.
  20. */
  21. LeftButtonMode(inttext,booleanenabled){
  22. this.text=text;
  23. this.enabled=enabled;
  24. }
  25. finalinttext;
  26. finalbooleanenabled;
  27. }
  28. /**
  29. *Thestatesoftherightbutton.
  30. */
  31. enumRightButtonMode{
  32. //继续
  33. Continue(R.string.lockpattern_continue_button_text,true),
  34. //继续时禁用
  35. ContinueDisabled(R.string.lockpattern_continue_button_text,false),
  36. //确认
  37. Confirm(R.string.lockpattern_confirm_button_text,true),
  38. //确认是禁用
  39. ConfirmDisabled(R.string.lockpattern_confirm_button_text,false),
  40. //OK
  41. Ok(android.R.string.ok,true);
  42. /**
  43. *@paramtext
  44. *Thedisplayedtextforthismode.
  45. *@paramenabled
  46. *Whetherthebuttonshouldbeenabled.
  47. */
  48. RightButtonMode(inttext,booleanenabled){
  49. this.text=text;
  50. this.enabled=enabled;
  51. }
  52. finalinttext;
  53. finalbooleanenabled;
  54. }

就这样,只要LockPatternView的状态一发生改变,就会动态改变底部两个Button的文字和状态。很简洁,逻辑性很强。

3.第三个个人觉得比较有用的就是加密这一块了,为了以后方便使用,我把图案加密和字符加密分成两个工具类:LockPatternUtils和LockPasswordUtils两个文件,本文使用到的是LockPatternUtils。其实所谓的图案加密也是将其通过SHA-1加密转化成二进制数再保存到文件中(原生系统保存在/system/目录下,我这里没有权限,就保存到本应用目录下),解密时,也是将获取到用户的输入通过同样的方法加密,再与保存到文件中的对比,相同则密码正确,不同则密码错误。关健代码就是以下4个函数:

[java] view plain copy
  1. /**
  2. *Serializeapattern.加密
  3. *
  4. *@parampattern
  5. *Thepattern.
  6. *@returnThepatterninstringform.
  7. */
  8. publicstaticStringpatternToString(List<LockPatternView.Cell>pattern){
  9. if(pattern==null){
  10. return"";
  11. }
  12. finalintpatternSize=pattern.size();
  13. byte[]res=newbyte[patternSize];
  14. for(inti=0;i<patternSize;i++){
  15. LockPatternView.Cellcell=pattern.get(i);
  16. res[i]=(byte)(cell.getRow()*3+cell.getColumn());
  17. }
  18. returnnewString(res);
  19. }
  20. /**
  21. *Savealockpattern.
  22. *
  23. *@parampattern
  24. *Thenewpatterntosave.
  25. *@paramisFallback
  26. *Specifiesifthisisafallbacktobiometricweak
  27. */
  28. publicvoidsaveLockPattern(List<LockPatternView.Cell>pattern){
  29. //Computethehash
  30. finalbyte[]hash=LockPatternUtils.patternToHash(pattern);
  31. try{
  32. //Writethehashtofile
  33. RandomAccessFileraf=newRandomAccessFile(sLockPatternFilename,
  34. "rwd");
  35. //Truncatethefileifpatternisnull,toclearthelock
  36. if(pattern==null){
  37. raf.setLength(0);
  38. }else{
  39. raf.write(hash,0,hash.length);
  40. }
  41. raf.close();
  42. }catch(FileNotFoundExceptionfnfe){
  43. //Cantdomuch,unlesswewanttofailovertousingthesettings
  44. //provider
  45. Log.e(TAG,"Unabletosavelockpatternto"+sLockPatternFilename);
  46. }catch(IOExceptionioe){
  47. //Cantdomuch
  48. Log.e(TAG,"Unabletosavelockpatternto"+sLockPatternFilename);
  49. }
  50. }
  51. /*
  52. *GenerateanSHA-1hashforthepattern.Notthemostsecure,butitisat
  53. *leastasecondlevelofprotection.Firstlevelisthatthefileisina
  54. *locationonlyreadablebythesystemprocess.
  55. *
  56. *@parampatternthegesturepattern.
  57. *
  58. *@returnthehashofthepatterninabytearray.
  59. */
  60. privatestaticbyte[]patternToHash(List<LockPatternView.Cell>pattern){
  61. if(pattern==null){
  62. returnnull;
  63. }
  64. finalintpatternSize=pattern.size();
  65. byte[]res=newbyte[patternSize];
  66. for(inti=0;i<patternSize;i++){
  67. LockPatternView.Cellcell=pattern.get(i);
  68. res[i]=(byte)(cell.getRow()*3+cell.getColumn());
  69. }
  70. try{
  71. MessageDigestmd=MessageDigest.getInstance("SHA-1");
  72. byte[]hash=md.digest(res);
  73. returnhash;
  74. }catch(NoSuchAlgorithmExceptionnsa){
  75. returnres;
  76. }
  77. }
  78. /**
  79. *Checktoseeifapatternmatchesthesavedpattern.Ifnopattern
  80. *exists,alwaysreturnstrue.
  81. *
  82. *@parampattern
  83. *Thepatterntocheck.
  84. *@returnWhetherthepatternmatchesthestoredone.
  85. */
  86. publicbooleancheckPattern(List<LockPatternView.Cell>pattern){
  87. try{
  88. //Readallthebytesfromthefile
  89. RandomAccessFileraf=newRandomAccessFile(sLockPatternFilename,
  90. "r");
  91. finalbyte[]stored=newbyte[(int)raf.length()];
  92. intgot=raf.read(stored,0,stored.length);
  93. raf.close();
  94. if(got<=0){
  95. returntrue;
  96. }
  97. //Comparethehashfromthefilewiththeenteredpattern'shash
  98. returnArrays.equals(stored,
  99. LockPatternUtils.patternToHash(pattern));
  100. }catch(FileNotFoundExceptionfnfe){
  101. returntrue;
  102. }catch(IOExceptionioe){
  103. returntrue;
  104. }
  105. }

好了,代码就分析到这里,非常感谢你看到了文章末尾,很晚了,睡觉去,如果大家有什么问题或建议,欢迎留言,一起讨论,谢谢!


19
1
主题推荐
android 手机 arraylist framework animation
猜你在找
android 三种实现水平向滑动方式(ViewPager、ViewFilpper、ViewFlow)的比较
Android通过Runtime.getRuntime().exec实现Ping和Traceroute命令时readLine阻塞问题解决
WEBGL中文教程-第五课-为3d图像添加纹理-翻译于外文
linux shell 比较两个浮点数
LayoutInflater 详解
再谈协方差矩阵之主成分分析
android UEventObserver的用法
二叉树中路径和为某整数的所有路径
Android开发—数据库应用—添加列表活动(ListActivity)--分析记事本程序
Android项目实战--手机卫士34--流量管理
查看评论
11楼 autumn_xl昨天 19:16发表[回复]
感谢提供 源码帮助我们这些 初学者
10楼 tessoo2014-07-28 14:30发表[回复]
亲 qq 多少 想请你做我们的签约讲师 我的电话18611277391
9楼 jackly_n2014-06-13 13:25发表[回复]
代码很清晰
8楼 时熊猫Time2014-05-26 16:07发表[回复]
谢谢作者分享
7楼 android00123452014-05-18 21:20发表[回复]
期待楼主更多的更精彩的博文,楼主杂不更新了。期待呀
6楼 params2014-05-16 17:22发表[回复]
太厉害了, 谢谢!
5楼 蓝槐魂2014-03-13 12:10发表[回复]
果断大牛啊,顶起
4楼 冒冒爱编程2013-12-30 14:57发表[回复]
大神你很强,小白的楷模!
3楼 彼岸此岸你好吗2013-12-26 22:54发表[回复]
顶起!支持下!
2楼 xiaoxueheha2013-12-02 11:17发表[回复]
FTP服务器bug问题:如果文件中含有中文(如文件名:XX(破解版).apk),当下载文件时没有问题,但上传时报错。电脑操作系统为win7
1楼 xiaoxueheha2013-11-29 09:57发表[回复][引用][举报]
楼主啊,你什么时候把你的高仿雅虎天气布局和你的简洁天气结合呢,很期待那应用哦
Re: weidi19892013-11-29 10:00发表[回复][引用][举报]
回复xiaoxueheha:遇到了几个比较严重的问题,一直没有找到解决办法,就停下来很久了。呵呵
Re: xiaoxueheha2013-11-29 10:24发表[回复][引用][举报]
回复weidi1989:一直在用你那天气软件哦,所以好奇的问一下哦 真的很不错哦 ,希望有更好的哦,谢谢

更多相关文章

  1. Android屏幕点亮(常亮)及屏幕解锁和锁定
  2. android edittxet 设置可点击不可编辑状态
  3. Android获取、设置Wifi状态
  4. Android 标题栏、状态栏隐藏
  5. Android软键盘之代码改变软键盘状态
  6. Android TabLayout设置选中状态标题字体大小,粗细
  7. android亮屏、暗屏、解锁、关闭系统对话的监听事件
  8. Android图形解锁的绘制

随机推荐

  1. android开发的3种方式
  2. android修炼进阶之法
  3. android studio项目转ADT的辛酸历程
  4. Android(安卓)内存管理介绍
  5. Android 数据存储五种方式使用与总结
  6. Android M 差分包的制作流程
  7. Android 切换主题 (二)
  8. android学习中遇到的问题
  9. Android:Intent 显示和隐式 学习
  10. 深入理解Android消息处理系统――Looper