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系统,炫一下,呵呵):



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(安卓)标题栏、状态栏隐藏
  3. Android获取、设置Wifi状态
  4. android edittxet 设置可点击不可编辑状态
  5. 【Android】Android(安卓)SurfaceFlinger之BufferQueue
  6. Android软键盘不能正确显示
  7. android shape的使用
  8. Android(安卓)Weekly Notes Issue #232
  9. android应用 小试牛刀 开发自己的应用程序就是这么简单

随机推荐

  1. 实现对特定内容改变字体颜色的TextView
  2. Android(安卓)NDK 开发教程三:Hello JNI
  3. 5款最常用的Android测试框架
  4. android学习——GridView实现主界面布局
  5. Flutter 配置打包
  6. Android(安卓)面试必问高级知识点(2021)
  7. Android侧滑菜单DrawerLayout的使用
  8. Android启动画面实现
  9. Android(安卓)Apk打包的流程
  10. android开发之Android(安卓)ActionBar完