Android之高仿手机QQ图案解锁
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 2013-11-29 00:39 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
- @Override
- protectedvoidonDraw(Canvascanvas){
- finalArrayList<Cell>pattern=mPattern;
- finalintcount=pattern.size();
- finalboolean[][]drawLookup=mPatternDrawLookup;
- if(mPatternDisplayMode==DisplayMode.Animate){
- //figureoutwhichcirclestodraw
- //+1sowepauseoncompletepattern
- finalintoneCycle=(count+1)*MILLIS_PER_CIRCLE_ANIMATING;
- finalintspotInCycle=(int)(SystemClock.elapsedRealtime()-mAnimatingPeriodStart)
- %oneCycle;
- finalintnumCircles=spotInCycle/MILLIS_PER_CIRCLE_ANIMATING;
- clearPatternDrawLookup();
- for(inti=0;i<numCircles;i++){
- finalCellcell=pattern.get(i);
- drawLookup[cell.getRow()][cell.getColumn()]=true;
- }
- //figureoutinprogressportionofghostingline
- finalbooleanneedToUpdateInProgressPoint=numCircles>0
- &&numCircles<count;
- if(needToUpdateInProgressPoint){
- finalfloatpercentageOfNextCircle=((float)(spotInCycle%MILLIS_PER_CIRCLE_ANIMATING))
- /MILLIS_PER_CIRCLE_ANIMATING;
- finalCellcurrentCell=pattern.get(numCircles-1);
- finalfloatcenterX=getCenterXForColumn(currentCell.column);
- finalfloatcenterY=getCenterYForRow(currentCell.row);
- finalCellnextCell=pattern.get(numCircles);
- finalfloatdx=percentageOfNextCircle
- *(getCenterXForColumn(nextCell.column)-centerX);
- finalfloatdy=percentageOfNextCircle
- *(getCenterYForRow(nextCell.row)-centerY);
- mInProgressX=centerX+dx;
- mInProgressY=centerY+dy;
- }
- //TODO:Infiniteloophere...
- invalidate();
- }
- finalfloatsquareWidth=mSquareWidth;
- finalfloatsquareHeight=mSquareHeight;
- floatradius=(squareWidth*mDiameterFactor*0.5f);
- mPathPaint.setStrokeWidth(radius);
- finalPathcurrentPath=mCurrentPath;
- currentPath.rewind();
- //TODO:thepathshouldbecreatedandcachedeverytimewehit-detect
- //acell
- //onlythelastsegmentofthepathshouldbecomputedhere
- //drawthepathofthepattern(unlesstheuserisinprogress,and
- //weareinstealthmode)
- finalbooleandrawPath=(!mInStealthMode||mPatternDisplayMode==DisplayMode.Wrong);
- //drawthearrowsassociatedwiththepath(unlesstheuserisin
- //progress,and
- //weareinstealthmode)
- booleanoldFlag=(mPaint.getFlags()&Paint.FILTER_BITMAP_FLAG)!=0;
- mPaint.setFilterBitmap(true);//drawwithhigherqualitysincewe
- //renderwithtransforms
- //drawthelines
- if(drawPath){
- booleananyCircles=false;
- for(inti=0;i<count;i++){
- Cellcell=pattern.get(i);
- //onlydrawthepartofthepatternstoredin
- //thelookuptable(thisisonlydifferentinthecase
- //ofanimation).
- if(!drawLookup[cell.row][cell.column]){
- break;
- }
- anyCircles=true;
- floatcenterX=getCenterXForColumn(cell.column);
- floatcenterY=getCenterYForRow(cell.row);
- if(i==0){
- currentPath.moveTo(centerX,centerY);
- }else{
- currentPath.lineTo(centerX,centerY);
- }
- }
- //addlastinprogresssection
- if((mPatternInProgress||mPatternDisplayMode==DisplayMode.Animate)
- &&anyCircles){
- currentPath.lineTo(mInProgressX,mInProgressY);
- }
- //changthelinecolorindifferentDisplayMode
- if(mPatternDisplayMode==DisplayMode.Wrong)
- mPathPaint.setColor(Color.RED);
- else
- mPathPaint.setColor(Color.YELLOW);
- canvas.drawPath(currentPath,mPathPaint);
- }
- //drawthecircles
- finalintpaddingTop=getPaddingTop();
- finalintpaddingLeft=getPaddingLeft();
- for(inti=0;i<3;i++){
- floattopY=paddingTop+i*squareHeight;
- //floatcenterY=mPaddingTop+i*mSquareHeight+(mSquareHeight
- ///2);
- for(intj=0;j<3;j++){
- floatleftX=paddingLeft+j*squareWidth;
- drawCircle(canvas,(int)leftX,(int)topY,drawLookup[i][j]);
- }
- }
- mPaint.setFilterBitmap(oldFlag);//restoredefaultflag
- }
2.第二个值得学习的地方是(代码来自设置应用中):在创建解锁图案时的枚举使用,原生代码中使用了很多枚举,将绘制图案时的状态、底部两个按钮状态、顶部一个TextView显示的提示文字都紧密的联系起来。因此,只用监听LockPatternView动态变化,对应改变底部Button和顶部TextView的状态即可实现联动,简单的方法可以实现很多代码才能实现的逻辑,个人很喜欢。
①全局的状态:
[java] view plain copy
- /**
- *Keeptrackinternallyofwheretheuserisinchoosingapattern.
- */
- protectedenumStage{
- //初始状态
- Introduction(R.string.lockpattern_recording_intro_header,
- LeftButtonMode.Cancel,RightButtonMode.ContinueDisabled,
- ID_EMPTY_MESSAGE,true),
- //帮助状态
- HelpScreen(R.string.lockpattern_settings_help_how_to_record,
- LeftButtonMode.Gone,RightButtonMode.Ok,ID_EMPTY_MESSAGE,
- false),
- //绘制过短
- ChoiceTooShort(R.string.lockpattern_recording_incorrect_too_short,
- LeftButtonMode.Retry,RightButtonMode.ContinueDisabled,
- ID_EMPTY_MESSAGE,true),
- //第一次绘制图案
- FirstChoiceValid(R.string.lockpattern_pattern_entered_header,
- LeftButtonMode.Retry,RightButtonMode.Continue,
- ID_EMPTY_MESSAGE,false),
- //需要再次绘制确认
- NeedToConfirm(R.string.lockpattern_need_to_confirm,
- LeftButtonMode.Cancel,RightButtonMode.ConfirmDisabled,
- ID_EMPTY_MESSAGE,true),
- //确认出错
- ConfirmWrong(R.string.lockpattern_need_to_unlock_wrong,
- LeftButtonMode.Cancel,RightButtonMode.ConfirmDisabled,
- ID_EMPTY_MESSAGE,true),
- //选择确认
- ChoiceConfirmed(R.string.lockpattern_pattern_confirmed_header,
- LeftButtonMode.Cancel,RightButtonMode.Confirm,
- ID_EMPTY_MESSAGE,false);
- /**
- *@paramheaderMessage
- *Themessagedisplayedatthetop.
- *@paramleftMode
- *Themodeoftheleftbutton.
- *@paramrightMode
- *Themodeoftherightbutton.
- *@paramfooterMessage
- *Thefootermessage.
- *@parampatternEnabled
- *Whetherthepatternwidgetisenabled.
- */
- Stage(intheaderMessage,LeftButtonModeleftMode,
- RightButtonModerightMode,intfooterMessage,
- booleanpatternEnabled){
- this.headerMessage=headerMessage;
- this.leftMode=leftMode;
- this.rightMode=rightMode;
- this.footerMessage=footerMessage;
- this.patternEnabled=patternEnabled;
- }
- finalintheaderMessage;
- finalLeftButtonModeleftMode;
- finalRightButtonModerightMode;
- finalintfooterMessage;
- finalbooleanpatternEnabled;
- }
②.底部两个按钮的状态枚举:
[java] view plain copy
- /**
- *Thestatesoftheleftfooterbutton.
- */
- enumLeftButtonMode{
- //取消
- Cancel(android.R.string.cancel,true),
- //取消时禁用
- CancelDisabled(android.R.string.cancel,false),
- //重试
- Retry(R.string.lockpattern_retry_button_text,true),
- //重试时禁用
- RetryDisabled(R.string.lockpattern_retry_button_text,false),
- //消失
- Gone(ID_EMPTY_MESSAGE,false);
- /**
- *@paramtext
- *Thedisplayedtextforthismode.
- *@paramenabled
- *Whetherthebuttonshouldbeenabled.
- */
- LeftButtonMode(inttext,booleanenabled){
- this.text=text;
- this.enabled=enabled;
- }
- finalinttext;
- finalbooleanenabled;
- }
- /**
- *Thestatesoftherightbutton.
- */
- enumRightButtonMode{
- //继续
- Continue(R.string.lockpattern_continue_button_text,true),
- //继续时禁用
- ContinueDisabled(R.string.lockpattern_continue_button_text,false),
- //确认
- Confirm(R.string.lockpattern_confirm_button_text,true),
- //确认是禁用
- ConfirmDisabled(R.string.lockpattern_confirm_button_text,false),
- //OK
- Ok(android.R.string.ok,true);
- /**
- *@paramtext
- *Thedisplayedtextforthismode.
- *@paramenabled
- *Whetherthebuttonshouldbeenabled.
- */
- RightButtonMode(inttext,booleanenabled){
- this.text=text;
- this.enabled=enabled;
- }
- finalinttext;
- finalbooleanenabled;
- }
就这样,只要LockPatternView的状态一发生改变,就会动态改变底部两个Button的文字和状态。很简洁,逻辑性很强。
3.第三个个人觉得比较有用的就是加密这一块了,为了以后方便使用,我把图案加密和字符加密分成两个工具类:LockPatternUtils和LockPasswordUtils两个文件,本文使用到的是LockPatternUtils。其实所谓的图案加密也是将其通过SHA-1加密转化成二进制数再保存到文件中(原生系统保存在/system/目录下,我这里没有权限,就保存到本应用目录下),解密时,也是将获取到用户的输入通过同样的方法加密,再与保存到文件中的对比,相同则密码正确,不同则密码错误。关健代码就是以下4个函数:
[java] view plain copy
- /**
- *Serializeapattern.加密
- *
- *@parampattern
- *Thepattern.
- *@returnThepatterninstringform.
- */
- publicstaticStringpatternToString(List<LockPatternView.Cell>pattern){
- if(pattern==null){
- return"";
- }
- finalintpatternSize=pattern.size();
- byte[]res=newbyte[patternSize];
- for(inti=0;i<patternSize;i++){
- LockPatternView.Cellcell=pattern.get(i);
- res[i]=(byte)(cell.getRow()*3+cell.getColumn());
- }
- returnnewString(res);
- }
- /**
- *Savealockpattern.
- *
- *@parampattern
- *Thenewpatterntosave.
- *@paramisFallback
- *Specifiesifthisisafallbacktobiometricweak
- */
- publicvoidsaveLockPattern(List<LockPatternView.Cell>pattern){
- //Computethehash
- finalbyte[]hash=LockPatternUtils.patternToHash(pattern);
- try{
- //Writethehashtofile
- RandomAccessFileraf=newRandomAccessFile(sLockPatternFilename,
- "rwd");
- //Truncatethefileifpatternisnull,toclearthelock
- if(pattern==null){
- raf.setLength(0);
- }else{
- raf.write(hash,0,hash.length);
- }
- raf.close();
- }catch(FileNotFoundExceptionfnfe){
- //Cantdomuch,unlesswewanttofailovertousingthesettings
- //provider
- Log.e(TAG,"Unabletosavelockpatternto"+sLockPatternFilename);
- }catch(IOExceptionioe){
- //Cantdomuch
- Log.e(TAG,"Unabletosavelockpatternto"+sLockPatternFilename);
- }
- }
- /*
- *GenerateanSHA-1hashforthepattern.Notthemostsecure,butitisat
- *leastasecondlevelofprotection.Firstlevelisthatthefileisina
- *locationonlyreadablebythesystemprocess.
- *
- *@parampatternthegesturepattern.
- *
- *@returnthehashofthepatterninabytearray.
- */
- privatestaticbyte[]patternToHash(List<LockPatternView.Cell>pattern){
- if(pattern==null){
- returnnull;
- }
- finalintpatternSize=pattern.size();
- byte[]res=newbyte[patternSize];
- for(inti=0;i<patternSize;i++){
- LockPatternView.Cellcell=pattern.get(i);
- res[i]=(byte)(cell.getRow()*3+cell.getColumn());
- }
- try{
- MessageDigestmd=MessageDigest.getInstance("SHA-1");
- byte[]hash=md.digest(res);
- returnhash;
- }catch(NoSuchAlgorithmExceptionnsa){
- returnres;
- }
- }
- /**
- *Checktoseeifapatternmatchesthesavedpattern.Ifnopattern
- *exists,alwaysreturnstrue.
- *
- *@parampattern
- *Thepatterntocheck.
- *@returnWhetherthepatternmatchesthestoredone.
- */
- publicbooleancheckPattern(List<LockPatternView.Cell>pattern){
- try{
- //Readallthebytesfromthefile
- RandomAccessFileraf=newRandomAccessFile(sLockPatternFilename,
- "r");
- finalbyte[]stored=newbyte[(int)raf.length()];
- intgot=raf.read(stored,0,stored.length);
- raf.close();
- if(got<=0){
- returntrue;
- }
- //Comparethehashfromthefilewiththeenteredpattern'shash
- returnArrays.equals(stored,
- LockPatternUtils.patternToHash(pattern));
- }catch(FileNotFoundExceptionfnfe){
- returntrue;
- }catch(IOExceptionioe){
- returntrue;
- }
- }
好了,代码就分析到这里,非常感谢你看到了文章末尾,很晚了,睡觉去,如果大家有什么问题或建议,欢迎留言,一起讨论,谢谢!
- 顶
- 19
- 踩
- 1
- 猜你在找
- 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:一直在用你那天气软件哦,所以好奇的问一下哦 真的很不错哦 ,希望有更好的哦,谢谢
更多相关文章
- Android沉浸式状态栏
- Android(安卓)标题栏、状态栏隐藏
- Android获取、设置Wifi状态
- android edittxet 设置可点击不可编辑状态
- 【Android】Android(安卓)SurfaceFlinger之BufferQueue
- Android软键盘不能正确显示
- android shape的使用
- Android(安卓)Weekly Notes Issue #232
- android应用 小试牛刀 开发自己的应用程序就是这么简单