Android仿手机QQ图案解锁功能

本文实例为大家分享了Android仿手机QQ图案解锁的具体代码,供大家参考,具体内容如下

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

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

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

关健部分代码onDraw函数:

@Override protected void onDraw(Canvas canvas) { final ArrayList<Cell> pattern = mPattern; final int count = pattern.size(); final boolean[][] drawLookup = mPatternDrawLookup; if (mPatternDisplayMode == DisplayMode.Animate) { // figure out which circles to draw // + 1 so we pause on complete pattern final int oneCycle = (count + 1) * MILLIS_PER_CIRCLE_ANIMATING; final int spotInCycle = (int) (SystemClock.elapsedRealtime() - mAnimatingPeriodStart) % oneCycle; final int numCircles = spotInCycle / MILLIS_PER_CIRCLE_ANIMATING; clearPatternDrawLookup(); for (int i = 0; i < numCircles; i++) { final Cell cell = pattern.get(i); drawLookup[cell.getRow()][cell.getColumn()] = true; } // figure out in progress portion of ghosting line final boolean needToUpdateInProgressPoint = numCircles > 0 && numCircles < count; if (needToUpdateInProgressPoint) { final float percentageOfNextCircle = ((float) (spotInCycle % MILLIS_PER_CIRCLE_ANIMATING)) / MILLIS_PER_CIRCLE_ANIMATING; final Cell currentCell = pattern.get(numCircles - 1); final float centerX = getCenterXForColumn(currentCell.column); final float centerY = getCenterYForRow(currentCell.row); final Cell nextCell = pattern.get(numCircles); final float dx = percentageOfNextCircle * (getCenterXForColumn(nextCell.column) - centerX); final float dy = percentageOfNextCircle * (getCenterYForRow(nextCell.row) - centerY); mInProgressX = centerX + dx; mInProgressY = centerY + dy; } // TODO: Infinite loop here... invalidate(); } final float squareWidth = mSquareWidth; final float squareHeight = mSquareHeight; float radius = (squareWidth * mDiameterFactor * 0.5f); mPathPaint.setStrokeWidth(radius); final Path currentPath = mCurrentPath; currentPath.rewind(); // TODO: the path should be created and cached every time we hit-detect // a cell // only the last segment of the path should be computed here // draw the path of the pattern (unless the user is in progress, and // we are in stealth mode) final boolean drawPath = (!mInStealthMode || mPatternDisplayMode == DisplayMode.Wrong); // draw the arrows associated with the path (unless the user is in // progress, and // we are in stealth mode) boolean oldFlag = (mPaint.getFlags() & Paint.FILTER_BITMAP_FLAG) != 0; mPaint.setFilterBitmap(true); // draw with higher quality since we // render with transforms // draw the lines if (drawPath) { boolean anyCircles = false; for (int i = 0; i < count; i++) { Cell cell = pattern.get(i); // only draw the part of the pattern stored in // the lookup table (this is only different in the case // of animation). if (!drawLookup[cell.row][cell.column]) { break; } anyCircles = true; float centerX = getCenterXForColumn(cell.column); float centerY = getCenterYForRow(cell.row); if (i == 0) { currentPath.moveTo(centerX, centerY); } else { currentPath.lineTo(centerX, centerY); } } // add last in progress section if ((mPatternInProgress || mPatternDisplayMode == DisplayMode.Animate) && anyCircles) { currentPath.lineTo(mInProgressX, mInProgressY); } // chang the line color in different DisplayMode if (mPatternDisplayMode == DisplayMode.Wrong) mPathPaint.setColor(Color.RED); else mPathPaint.setColor(Color.YELLOW); canvas.drawPath(currentPath, mPathPaint); } // draw the circles final int paddingTop = getPaddingTop(); final int paddingLeft = getPaddingLeft(); for (int i = 0; i < 3; i++) { float topY = paddingTop + i * squareHeight; // float centerY = mPaddingTop + i * mSquareHeight + (mSquareHeight // / 2); for (int j = 0; j < 3; j++) { float leftX = paddingLeft + j * squareWidth; drawCircle(canvas, (int) leftX, (int) topY, drawLookup[i][j]); } } mPaint.setFilterBitmap(oldFlag); // restore default flag }

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

①全局的状态:

/** * Keep track internally of where the user is in choosing a pattern. */ protected enum Stage { // 初始状态 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); /** * @param headerMessage * The message displayed at the top. * @param leftMode * The mode of the left button. * @param rightMode * The mode of the right button. * @param footerMessage * The footer message. * @param patternEnabled * Whether the pattern widget is enabled. */ Stage(int headerMessage, LeftButtonMode leftMode, RightButtonMode rightMode, int footerMessage, boolean patternEnabled) { this.headerMessage = headerMessage; this.leftMode = leftMode; this.rightMode = rightMode; this.footerMessage = footerMessage; this.patternEnabled = patternEnabled; } final int headerMessage; final LeftButtonMode leftMode; final RightButtonMode rightMode; final int footerMessage; final boolean patternEnabled; }

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

/** * The states of the left footer button. */ enum LeftButtonMode { // 取消 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); /** * @param text * The displayed text for this mode. * @param enabled * Whether the button should be enabled. */ LeftButtonMode(int text, boolean enabled) { this.text = text; this.enabled = enabled; } final int text; final boolean enabled; } /** * The states of the right button. */ enum RightButtonMode { // 继续 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); /** * @param text * The displayed text for this mode. * @param enabled * Whether the button should be enabled. */ RightButtonMode(int text, boolean enabled) { this.text = text; this.enabled = enabled; } final int text; final boolean enabled; }

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

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

/** * Serialize a pattern. 加密 * * @param pattern * The pattern. * @return The pattern in string form. */ public static String patternToString(List<LockPatternView.Cell> pattern) { if (pattern == null) { return ""; } final int patternSize = pattern.size(); byte[] res = new byte[patternSize]; for (int i = 0; i < patternSize; i++) { LockPatternView.Cell cell = pattern.get(i); res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); } return new String(res); } /** * Save a lock pattern. * * @param pattern * The new pattern to save. * @param isFallback * Specifies if this is a fallback to biometric weak */ public void saveLockPattern(List<LockPatternView.Cell> pattern) { // Compute the hash final byte[] hash = LockPatternUtils.patternToHash(pattern); try { // Write the hash to file RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "rwd"); // Truncate the file if pattern is null, to clear the lock if (pattern == null) { raf.setLength(0); } else { raf.write(hash, 0, hash.length); } raf.close(); } catch (FileNotFoundException fnfe) { // Cant do much, unless we want to fail over to using the settings // provider Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename); } catch (IOException ioe) { // Cant do much Log.e(TAG, "Unable to save lock pattern to " + sLockPatternFilename); } } /* * Generate an SHA-1 hash for the pattern. Not the most secure, but it is at * least a second level of protection. First level is that the file is in a * location only readable by the system process. * * @param pattern the gesture pattern. * * @return the hash of the pattern in a byte array. */ private static byte[] patternToHash(List<LockPatternView.Cell> pattern) { if (pattern == null) { return null; } final int patternSize = pattern.size(); byte[] res = new byte[patternSize]; for (int i = 0; i < patternSize; i++) { LockPatternView.Cell cell = pattern.get(i); res[i] = (byte) (cell.getRow() * 3 + cell.getColumn()); } try { MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] hash = md.digest(res); return hash; } catch (NoSuchAlgorithmException nsa) { return res; } } /** * Check to see if a pattern matches the saved pattern. If no pattern * exists, always returns true. * * @param pattern * The pattern to check. * @return Whether the pattern matches the stored one. */ public boolean checkPattern(List<LockPatternView.Cell> pattern) { try { // Read all the bytes from the file RandomAccessFile raf = new RandomAccessFile(sLockPatternFilename, "r"); final byte[] stored = new byte[(int) raf.length()]; int got = raf.read(stored, 0, stored.length); raf.close(); if (got <= 0) { return true; } // Compare the hash from the file with the entered pattern's hash return Arrays.equals(stored, LockPatternUtils.patternToHash(pattern)); } catch (FileNotFoundException fnfe) { return true; } catch (IOException ioe) { return true; } }

好了,代码就分析到这里,非常感谢你看到了文章末尾。

本文源码(utf-8编码):Android仿手机QQ图案解锁

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

时间: 2024-09-22 21:38:49

Android仿手机QQ图案解锁功能的相关文章

Android仿支付宝手势密码解锁功能_Android

Starting 创建手势密码可以查看 CreateGestureActivity.java 文件. 登陆验证手势密码可以看 GestureLoginActivity.java 文件. Features 使用了 JakeWharton/butterknife butterknife 使用了 ACache 来存储手势密码 /** * 保存手势密码 */ private void saveChosenPattern(List<LockPatternView.Cell> cells) { byte[

Android仿支付宝手势密码解锁功能

Starting 创建手势密码可以查看 CreateGestureActivity.java 文件. 登陆验证手势密码可以看 GestureLoginActivity.java 文件. Features 使用了 JakeWharton/butterknife butterknife 使用了 ACache 来存储手势密码 /** * 保存手势密码 */ private void saveChosenPattern(List<LockPatternView.Cell> cells) { byte[

Android仿微信QQ设置图形头像裁剪功能_Android

最近在做毕业设计,想有一个功能和QQ一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流. 而纵观现在主流的APP,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue)! 图片裁剪实现方式有两种,一种是利用系统自带的裁剪工具,一种是使用开源工具Cropper.本节就为大家带来如何使用系统自带的裁剪工具进行图片裁剪~ 还是先来个简单的运行图. 额,简单说下,我待会会把代码写成小demo分享给大家,在文章末尾会附上github链接,需要的可以自行下载~ 下面来简单分

Android仿微信QQ设置图形头像裁剪功能

最近在做毕业设计,想有一个功能和QQ一样可以裁剪头像并设置圆形头像,额,这是设计狮的一种潮流. 而纵观现在主流的APP,只要有用户系统这个功能,这个需求一般都是在(bu)劫(de)难(bu)逃(xue)! 图片裁剪实现方式有两种,一种是利用系统自带的裁剪工具,一种是使用开源工具Cropper.本节就为大家带来如何使用系统自带的裁剪工具进行图片裁剪~ 还是先来个简单的运行图. 额,简单说下,我待会会把代码写成小demo分享给大家,在文章末尾会附上github链接,需要的可以自行下载~ 下面来简单分

基于zepto.js实现仿手机QQ空间的大图查看组件ImageView.js详解

 这篇文章主要介绍了基于zepto.js实现仿手机QQ空间的大图查看组件ImageView.js的源码和使用方法,并附上一个使用ImageView.js的实例,这里分享给大家,有需要的小伙伴参考下.     调用方式 :ImageView(index,imgData) --index参数 为图片默认显示的索引值,类型 为Number --imaData参数 为图片url数组 ,类型为Array 使用之前要先引入 zepto.js 文件 ImageView.js具体代码如下:   代码如下: /*

手机QQ指纹解锁怎么设置?QQ指纹解锁设置方法

1)我们先在确定使用的是ios8系统并且ios中己打开了Touch ID解锁功能了,现在我们在QQ中点击[设置],在[设置]-[设备锁.账号安全]然后再找到[手势.指纹锁定]进入.(如下图所示)    2)然后打开的[手势.指纹设置]中我们就可以打开Touch ID指纹解锁功能即可使用了.   ps:这个QQ指纹解锁功能是iphone的ios8系统才有的一个功能哦,其它安卓手机暂时不可使用哦.

Android版手机QQ 5.0正式发布:更扁平

Android版手机QQ 5.0正式发布:更扁平就在刚刚,手机QQ官网微博宣布Android手机QQ 5.0正式上线,并附上了下载链接.相比当前的4.7版本,5.0给人的整体感受便是:更扁平轻便.操作更方便.功能更强大但无颠覆.相比手机QQ 4.X版,5.0的界面发生了相当大的改变,最主要特征便是Tab标签由原先的4个减为3个,界面更为清爽,默认配色也采用了蓝色色调,更为活泼.功能方面,5.0并没有变革性改进,主要是增强了音视频功能,比如可实现多人视频,在音频聊天时可随时切换到视频模式等等.此外

手机QQ匿名聊天功能在哪 手机QQ匿名聊天设置步骤

我们知道手机QQ匿名聊天功能必须安装最新版本手机QQ才可以的哦. 1.我们在手机中打开 QQ群聊 之后我们点击右上角的[头像]标志 2.之后我们找到[管理群] 进入,细节如下图所示 3.之后我们会看到如下图所示[允许群内匿名聊天]后的按钮 点击它. 4.之后我们找到  [允许群内匿名聊天]旁边会有开启绿色了哦. 5.好了现在我们进入到位 群聊界面,之后我们点击[更多]标志 打开进入 6.现在我们点击信息界面的[匿名]图标 之后打开进入 7.好了现在我们就已经进入匿名聊天模式, 这样就是黑色背景了

手机qq空间第一张照片功能在哪,QQ空间第一照怎么玩?

手机qq空间第一张照片功能在哪 打开手机QQ-QQ空间动态 然后找到"活动推广"然后点击进入即可看到你的第一张QQ空间照片了. 点进去 好了往下看可以看更多的关于QQ空间相册的一些历史记录哦,这些记录如果没有用就删除吧,留着好的就好坏了可能是麻烦哦,特别是像xx吉xx吉这种人呀.