├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zyyoona7 │ │ └── sample │ │ ├── DownTimer.java │ │ ├── LauncherActivity.java │ │ ├── LockActivity.java │ │ ├── LockFragment.java │ │ ├── LockSettingActivity.java │ │ └── MyApplication.java │ └── res │ ├── anim │ ├── cycle_4.xml │ ├── fade_ins.xml │ ├── push_bottom_in_2.xml │ └── shake.xml │ ├── layout │ ├── activity_launcher.xml │ ├── activity_lock.xml │ ├── activity_lock_setting.xml │ └── fragment_lock.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gesturelockview ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── zyyoona7 │ │ └── lock │ │ ├── ConvertUtils.java │ │ ├── Dot.java │ │ ├── GestureLockDisplayView.java │ │ ├── GestureLockLayout.java │ │ ├── ILockView.java │ │ ├── JDLockView.java │ │ ├── LockViewFactory.java │ │ └── QQLockView.java │ └── res │ └── values │ ├── attrs.xml │ └── strings.xml ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images ├── gesture1.gif └── gesture2.gif └── settings.gradle /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea 5 | .DS_Store 6 | /build 7 | /captures 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GestureLockView 2 | ### 手势解锁 3 | 修改[鸿洋的](http://blog.csdn.net/lmj623565791/article/details/36236113)手势解锁,继承RelativeLayout,可以自由定制手势点的样式的手势解锁 4 | ### 特性 5 | - 实现ILockView,自由定制样式 6 | - 支持修改/设置密码模式和验证密码模式 7 | ### 效果图 8 | ![](https://github.com/zyyoona7/GestureLockView/blob/master/images/gesture1.gif) 9 | ![](https://github.com/zyyoona7/GestureLockView/blob/master/images/gesture2.gif) 10 | ### 使用 11 | #### 1. 简单使用 12 | **设置/重置密码模式** 13 | 14 | 布局文件中: 15 | 16 | ```xml 17 | 22 | 23 | 24 | 29 | 30 | 37 | 38 | 39 | 44 | 45 | 46 | 47 | ``` 48 | 49 | java 代码中: 50 | 51 | ```java 52 | private void initViews() { 53 | mGestureLockLayout = (GestureLockLayout) findViewById(R.id.l_gesture_view); 54 | mLockDisplayView = (GestureLockDisplayView) findViewById(R.id.l_display_view); 55 | mSettingHintText = (TextView) findViewById(R.id.tv_setting_hint); 56 | //设置提示view 每行每列点的个数 57 | mLockDisplayView.setDotCount(3); 58 | //设置提示view 选中状态的颜色 59 | mLockDisplayView.setDotSelectedColor(Color.parseColor("#01A0E5")); 60 | //设置提示view 非选中状态的颜色 61 | mLockDisplayView.setDotUnSelectedColor(Color.TRANSPARENT); 62 | //设置手势解锁view 每行每列点的个数 63 | mGestureLockLayout.setDotCount(3); 64 | //设置手势解锁view 最少连接数 65 | mGestureLockLayout.setMinCount(3); 66 | //默认解锁样式为手Q手势解锁样式 67 | //mGestureLockLayout.setLockView(); 68 | //设置手势解锁view 模式为重置密码模式 69 | mGestureLockLayout.setMode(GestureLockLayout.RESET_MODE); 70 | } 71 | 72 | private void initEvents() { 73 | mGestureLockLayout.setOnLockResetListener(new GestureLockLayout.OnLockResetListener() { 74 | @Override 75 | public void onConnectCountUnmatched(int connectCount, int minCount) { 76 | //连接数小于最小连接数时调用 77 | 78 | mSettingHintText.setText("最少连接" + minCount + "个点"); 79 | resetGesture(); 80 | } 81 | 82 | @Override 83 | public void onFirstPasswordFinished(List answerList) { 84 | //第一次绘制手势成功时调用 85 | 86 | mSettingHintText.setText("确认解锁图案"); 87 | //将答案设置给提示view 88 | mLockDisplayView.setAnswer(answerList); 89 | //重置 90 | resetGesture(); 91 | } 92 | 93 | @Override 94 | public void onSetPasswordFinished(boolean isMatched, List answerList) { 95 | //第二次密码绘制成功时调用 96 | 97 | if (isMatched) { 98 | //两次答案一致,保存 99 | //do something 100 | } else { 101 | resetGesture(); 102 | } 103 | } 104 | }); 105 | } 106 | 107 | /** 108 | * 重置 109 | */ 110 | private void resetGesture() { 111 | mHandler.postDelayed(new Runnable() { 112 | @Override 113 | public void run() { 114 | mGestureLockLayout.resetGesture(); 115 | } 116 | }, 200); 117 | } 118 | ``` 119 | 120 | **验证密码模式** 121 | 122 | 布局文件中: 123 | 124 | ```xml 125 | 130 | 131 | 137 | 138 | 143 | 144 | 145 | 146 | ``` 147 | 148 | java 代码中: 149 | 150 | ```java 151 | private void initViews() { 152 | mGestureLockLayout = (GestureLockLayout) findViewById(R.id.l_lock_view); 153 | mHintText = (TextView) findViewById(R.id.tv_hint); 154 | //设置手势解锁模式为验证模式 155 | mGestureLockLayout.setMode(GestureLockLayout.VERIFY_MODE); 156 | //设置手势解锁每行每列点的个数 157 | mGestureLockLayout.setDotCount(3); 158 | //设置手势解锁最大尝试次数 默认 5 159 | mGestureLockLayout.setTryTimes(3); 160 | //设置手势解锁正确答案 161 | mGestureLockLayout.setAnswer(getCacheAnswer()); 162 | } 163 | 164 | private void initEvents() { 165 | mGestureLockLayout.setOnLockVerifyListener(new GestureLockLayout.OnLockVerifyListener() { 166 | @Override 167 | public void onGestureSelected(int id) { 168 | //每选中一个点时调用 169 | } 170 | 171 | @Override 172 | public void onGestureFinished(boolean isMatched) { 173 | //绘制手势解锁完成时调用 174 | 175 | if (isMatched) { 176 | //密码匹配 177 | } else { 178 | //不匹配 179 | mHintText.setText("还有" + mGestureLockLayout.getTryTimes() + "次机会"); 180 | resetGesture(); 181 | } 182 | } 183 | 184 | @Override 185 | public void onGestureTryTimesBoundary() { 186 | //超出最大尝试次数时调用 187 | 188 | mGestureLockLayout.setTouchable(false); 189 | } 190 | }); 191 | } 192 | 193 | private void resetGesture() { 194 | mHandler.postDelayed(new Runnable() { 195 | @Override 196 | public void run() { 197 | mGestureLockLayout.resetGesture(); 198 | } 199 | }, 200); 200 | } 201 | ``` 202 | 203 | #### 2. 自定义样式 204 | 205 | 实现 ILockView 接口: 206 | 207 | ```java 208 | public interface ILockView { 209 | 210 | //手势状态 211 | int NO_FINGER = 0; 212 | int FINGER_TOUCH = 1; 213 | int FINGER_UP_MATCHED = 2; 214 | int FINGER_UP_UN_MATCHED = 3; 215 | 216 | /** 217 | * 获取View 218 | * 219 | * @return 220 | */ 221 | View getView(); 222 | 223 | /** 224 | * 手指没触摸之前,初始状态 225 | */ 226 | void onNoFinger(); 227 | 228 | /** 229 | * 手指触摸,按下状态 230 | */ 231 | void onFingerTouch(); 232 | 233 | /** 234 | * 手指抬起,手势密码匹配状态 235 | */ 236 | void onFingerUpMatched(); 237 | 238 | /** 239 | * 手指抬起,手势密码不匹配状态 240 | */ 241 | void onFingerUpUnmatched(); 242 | } 243 | ``` 244 | 245 | iOS 版京东金融手势解锁样式: 246 | 247 | ```java 248 | public class JDLockView extends View implements ILockView { 249 | 250 | private Paint mPaint; 251 | private int mCurrentState=NO_FINGER; 252 | private float mOuterRadius; 253 | private float mInnerRadius; 254 | 255 | public JDLockView(Context context) { 256 | this(context,null); 257 | } 258 | 259 | public JDLockView(Context context, @Nullable AttributeSet attrs) { 260 | super(context, attrs); 261 | init(context); 262 | } 263 | 264 | public void init(Context context){ 265 | mPaint=new Paint(); 266 | mPaint.setAntiAlias(true); 267 | } 268 | 269 | @Override 270 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 271 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 272 | int width = MeasureSpec.getSize(widthMeasureSpec); 273 | int height = MeasureSpec.getSize(heightMeasureSpec); 274 | width = width > height ? height : width; 275 | setMeasuredDimension(width, width); 276 | } 277 | 278 | @Override 279 | protected void onDraw(Canvas canvas) { 280 | super.onDraw(canvas); 281 | float space = 10; 282 | float x = getWidth() / 2; 283 | float y = getHeight() / 2; 284 | canvas.translate(x, y); 285 | mOuterRadius = x - space; 286 | mInnerRadius = (x - space) / 3; 287 | switch (mCurrentState) { 288 | case NO_FINGER: 289 | drawNoFinger(canvas); 290 | break; 291 | case FINGER_TOUCH: 292 | drawFingerTouch(canvas); 293 | break; 294 | case FINGER_UP_MATCHED: 295 | drawFingerUpMatched(canvas); 296 | break; 297 | case FINGER_UP_UN_MATCHED: 298 | drawFingerUpUnmatched(canvas); 299 | break; 300 | } 301 | } 302 | 303 | /** 304 | * 画无手指触摸状态 305 | * 306 | * @param canvas 307 | */ 308 | private void drawNoFinger(Canvas canvas) { 309 | mPaint.setStyle(Paint.Style.FILL); 310 | mPaint.setColor(Color.GRAY); 311 | canvas.drawCircle(0, 0, mInnerRadius, mPaint); 312 | } 313 | 314 | /** 315 | * 画手指触摸状态 316 | * 317 | * @param canvas 318 | */ 319 | private void drawFingerTouch(Canvas canvas) { 320 | mPaint.setStyle(Paint.Style.FILL); 321 | mPaint.setColor(Color.BLUE); 322 | canvas.drawCircle(0, 0, mInnerRadius, mPaint); 323 | mPaint.setStyle(Paint.Style.STROKE); 324 | mPaint.setStrokeWidth(ConvertUtils.dp2px(getContext(),1)); 325 | canvas.drawCircle(0, 0, mOuterRadius, mPaint); 326 | } 327 | 328 | /** 329 | * 画手指抬起,匹配状态 330 | * 331 | * @param canvas 332 | */ 333 | private void drawFingerUpMatched(Canvas canvas) { 334 | drawFingerTouch(canvas); 335 | } 336 | 337 | /** 338 | * 画手指抬起,不匹配状态 339 | * 340 | * @param canvas 341 | */ 342 | private void drawFingerUpUnmatched(Canvas canvas) { 343 | mPaint.setStyle(Paint.Style.FILL); 344 | mPaint.setColor(Color.RED); 345 | canvas.drawCircle(0, 0, mInnerRadius, mPaint); 346 | mPaint.setStyle(Paint.Style.STROKE); 347 | mPaint.setStrokeWidth(ConvertUtils.dp2px(getContext(),1)); 348 | canvas.drawCircle(0, 0, mOuterRadius, mPaint); 349 | } 350 | 351 | @Override 352 | public View getView() { 353 | return this; 354 | } 355 | 356 | @Override 357 | public void onNoFinger() { 358 | mCurrentState=NO_FINGER; 359 | postInvalidate(); 360 | } 361 | 362 | @Override 363 | public void onFingerTouch() { 364 | mCurrentState=FINGER_TOUCH; 365 | postInvalidate(); 366 | } 367 | 368 | @Override 369 | public void onFingerUpMatched() { 370 | mCurrentState=FINGER_UP_MATCHED; 371 | postInvalidate(); 372 | } 373 | 374 | @Override 375 | public void onFingerUpUnmatched() { 376 | mCurrentState=FINGER_UP_UN_MATCHED; 377 | postInvalidate(); 378 | } 379 | } 380 | ``` 381 | 382 | 使用的时候只需多调用一下下面方法: 383 | 384 | ```java 385 | //设置手势解锁样式 386 | mGestureLockLayout.setLockView(new LockViewFactory() { 387 | @Override 388 | public ILockView newLockView() { 389 | return new JDLockView(LockSettingActivity.this); 390 | } 391 | }); 392 | ``` 393 | 394 | 其他用法同 1 一致。 395 | ### License 396 | ``` 397 | Copyright 2017 zyyoona7 398 | 399 | Licensed under the Apache License, Version 2.0 (the "License"); 400 | you may not use this file except in compliance with the License. 401 | You may obtain a copy of the License at 402 | 403 | http://www.apache.org/licenses/LICENSE-2.0 404 | 405 | Unless required by applicable law or agreed to in writing, software 406 | distributed under the License is distributed on an "AS IS" BASIS, 407 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 408 | See the License for the specific language governing permissions and 409 | limitations under the License. 410 | ``` 411 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | buildToolsVersion '27.0.3' 6 | 7 | defaultConfig { 8 | applicationId "com.zyyoona7.sample" 9 | minSdkVersion 15 10 | targetSdkVersion 26 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | implementation fileTree(include: ['*.jar'], dir: 'libs') 24 | implementation project(':gesturelockview') 25 | implementation 'com.android.support.constraint:constraint-layout:1.1.2' 26 | api 'com.android.support:appcompat-v7:27.1.1' 27 | implementation 'me.yokeyword:fragmentation:1.3.5' 28 | 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\android-sdk-windows/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/sample/DownTimer.java: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.sample; 2 | 3 | import android.os.Handler; 4 | import android.os.Message; 5 | import android.os.SystemClock; 6 | 7 | /** 8 | * DownTimer 9 | * 10 | * @author cjj 11 | * @version 1.0 12 | * @category 倒计时工具类 13 | * @content 1.实例化后必须设置倒计时的总时间(totalTime)和每隔多久时间(intervalTime)回调
14 | * 2.有start()、 cancel()、 pause()、resume() 四个方法看方法就知道它的意思了 15 | */ 16 | public class DownTimer { 17 | private long totalTime = -1; 18 | private long intervalTime = 0; 19 | private long remainTime; 20 | private long systemAddTotalTime; 21 | private static final int TIME = 1; 22 | private TimeListener listener; 23 | private long curReminTime; 24 | private boolean isPause = false; 25 | 26 | public DownTimer() { 27 | } 28 | 29 | public void setIntervalTime(long intervalTime) { 30 | this.intervalTime = intervalTime; 31 | } 32 | 33 | public void setTotalTime(long totalTime) { 34 | this.totalTime = totalTime; 35 | } 36 | 37 | public long getIntervalTime() { 38 | return intervalTime; 39 | } 40 | 41 | public long getTotalTime() { 42 | return totalTime; 43 | } 44 | 45 | public void start() { 46 | if (totalTime <= 0 && intervalTime <= 0) { 47 | throw new RuntimeException("you must set the totalTime > 0 or intervalTime >0"); 48 | } 49 | 50 | systemAddTotalTime = SystemClock.elapsedRealtime() + totalTime; 51 | 52 | if (null != mHandler) 53 | mHandler.sendEmptyMessage(TIME); 54 | } 55 | 56 | public void cancel() { 57 | if (mHandler != null) { 58 | mHandler.removeMessages(TIME); 59 | mHandler = null; 60 | } 61 | 62 | } 63 | 64 | public void pause() { 65 | if (mHandler != null) { 66 | mHandler.removeMessages(TIME); 67 | isPause = true; 68 | curReminTime = remainTime; 69 | } 70 | 71 | } 72 | 73 | public void resume() { 74 | if (isPause == true) { 75 | isPause = false; 76 | totalTime = curReminTime; 77 | start(); 78 | } 79 | 80 | } 81 | 82 | private Handler mHandler = new Handler() { 83 | 84 | @Override 85 | public void handleMessage(Message msg) { 86 | switch (msg.what) { 87 | case TIME: 88 | if (!isPause) 89 | soloveTime(); 90 | break; 91 | case 2: 92 | isPause = true; 93 | break; 94 | default: 95 | break; 96 | } 97 | } 98 | }; 99 | 100 | 101 | private void soloveTime() { 102 | remainTime = systemAddTotalTime - SystemClock.elapsedRealtime(); 103 | if (remainTime <= 0) { 104 | if (listener != null) { 105 | listener.onFinish(); 106 | cancel(); 107 | } 108 | } else if (remainTime < intervalTime) { 109 | if (null != mHandler) 110 | mHandler.sendEmptyMessageDelayed(TIME, remainTime); 111 | } else { 112 | long curSystemTime = SystemClock.elapsedRealtime(); 113 | if (listener != null) { 114 | listener.onInterval(remainTime); 115 | } 116 | 117 | long delay = curSystemTime + intervalTime - SystemClock.elapsedRealtime(); 118 | 119 | while (delay < 0) delay += intervalTime; 120 | 121 | if (null != mHandler) { 122 | mHandler.sendEmptyMessageDelayed(TIME, delay); 123 | } 124 | } 125 | } 126 | 127 | public interface TimeListener { 128 | public void onFinish(); 129 | 130 | public void onInterval(long remainTime); 131 | } 132 | 133 | public void setTimerLiener(TimeListener listener) { 134 | this.listener = listener; 135 | } 136 | 137 | } -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/sample/LauncherActivity.java: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.sample; 2 | 3 | import android.content.Intent; 4 | import android.graphics.Color; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.text.TextUtils; 8 | import android.view.View; 9 | import android.widget.Button; 10 | import android.widget.Toast; 11 | 12 | import com.zyyoona7.lock.GestureLockLayout; 13 | import com.zyyoona7.lock.ILockView; 14 | import com.zyyoona7.lock.JDLockView; 15 | import com.zyyoona7.lock.LockViewFactory; 16 | import com.zyyoona7.lock.QQLockView; 17 | 18 | 19 | public class LauncherActivity extends AppCompatActivity { 20 | // TODO: 2017/7/10 展示更多样式 21 | 22 | private Button mSettingBtn; 23 | private Button mQQBtn; 24 | private Button mJDBtn; 25 | private GestureLockLayout mGestureLockLayout; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.activity_launcher); 31 | initViews(); 32 | initEvents(); 33 | } 34 | 35 | private void initViews(){ 36 | mSettingBtn = (Button) findViewById(R.id.btn_setting); 37 | mQQBtn= (Button) findViewById(R.id.btn_qq); 38 | mJDBtn= (Button) findViewById(R.id.btn_jd); 39 | mGestureLockLayout= (GestureLockLayout) findViewById(R.id.l_gesture_lock_view); 40 | mGestureLockLayout.setDotCount(3); 41 | mGestureLockLayout.setMode(GestureLockLayout.VERIFY_MODE); 42 | mGestureLockLayout.setTryTimes(100); 43 | mGestureLockLayout.setAnswer(0,1,2); 44 | 45 | } 46 | 47 | private void initEvents(){ 48 | mSettingBtn.setOnClickListener(new View.OnClickListener() { 49 | @Override 50 | public void onClick(View v) { 51 | goTo(LockSettingActivity.class); 52 | } 53 | }); 54 | 55 | mQQBtn.setOnClickListener(new View.OnClickListener() { 56 | @Override 57 | public void onClick(View v) { 58 | mGestureLockLayout.setLockView(new LockViewFactory() { 59 | @Override 60 | public ILockView newLockView() { 61 | return new QQLockView(LauncherActivity.this); 62 | } 63 | }); 64 | mGestureLockLayout.setPathWidth(2); 65 | mGestureLockLayout.setTouchedPathColor(Color.parseColor("#01A0E5")); 66 | mGestureLockLayout.setMatchedPathColor(Color.parseColor("#01A0E5")); 67 | mGestureLockLayout.setUnmatchedPathColor(Color.parseColor("#F7564A")); 68 | } 69 | }); 70 | 71 | mJDBtn.setOnClickListener(new View.OnClickListener() { 72 | @Override 73 | public void onClick(View v) { 74 | mGestureLockLayout.setLockView(new LockViewFactory() { 75 | @Override 76 | public ILockView newLockView() { 77 | return new JDLockView(LauncherActivity.this); 78 | } 79 | }); 80 | mGestureLockLayout.setPathWidth(1); 81 | mGestureLockLayout.setTouchedPathColor(Color.BLUE); 82 | mGestureLockLayout.setMatchedPathColor(Color.BLUE); 83 | mGestureLockLayout.setUnmatchedPathColor(Color.RED); 84 | } 85 | }); 86 | 87 | mGestureLockLayout.setOnLockVerifyListener(new GestureLockLayout.OnLockVerifyListener() { 88 | @Override 89 | public void onGestureSelected(int id) { 90 | 91 | } 92 | 93 | @Override 94 | public void onGestureFinished(boolean isMatched) { 95 | if (isMatched) { 96 | Toast.makeText(LauncherActivity.this,"密码正确",Toast.LENGTH_SHORT).show(); 97 | }else { 98 | Toast.makeText(LauncherActivity.this,"密码不正确",Toast.LENGTH_SHORT).show(); 99 | } 100 | } 101 | 102 | @Override 103 | public void onGestureTryTimesBoundary() { 104 | 105 | } 106 | }); 107 | } 108 | 109 | @Override 110 | protected void onResume() { 111 | super.onResume(); 112 | if (!TextUtils.isEmpty(MyApplication.getInstance().answer)&& !MyApplication.getInstance().isUnlock) { 113 | goTo(LockActivity.class); 114 | } 115 | } 116 | 117 | public void goTo(Class clazz) { 118 | Intent intent = new Intent(this, clazz); 119 | startActivity(intent); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/sample/LockActivity.java: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.sample; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.widget.TextView; 7 | 8 | import com.zyyoona7.lock.GestureLockLayout; 9 | 10 | import me.yokeyword.fragmentation.SupportActivity; 11 | 12 | public class LockActivity extends SupportActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_lock); 18 | 19 | loadRootFragment(R.id.fl_fragment_container,LockFragment.newInstance()); 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/sample/LockFragment.java: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.sample; 2 | 3 | import android.os.Bundle; 4 | import android.os.Handler; 5 | import android.support.annotation.NonNull; 6 | import android.support.annotation.Nullable; 7 | import android.view.LayoutInflater; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import android.widget.TextView; 11 | 12 | import com.zyyoona7.lock.GestureLockLayout; 13 | 14 | import me.yokeyword.fragmentation.SupportFragment; 15 | 16 | public class LockFragment extends SupportFragment { 17 | private GestureLockLayout mGestureLockLayout; 18 | private TextView mHintText; 19 | private Handler mHandler = new Handler(); 20 | 21 | public static LockFragment newInstance() { 22 | 23 | Bundle args = new Bundle(); 24 | 25 | LockFragment fragment = new LockFragment(); 26 | fragment.setArguments(args); 27 | return fragment; 28 | } 29 | 30 | @Nullable 31 | @Override 32 | public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { 33 | return inflater.inflate(R.layout.fragment_lock,container,false); 34 | } 35 | 36 | @Override 37 | public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { 38 | super.onViewCreated(view, savedInstanceState); 39 | initViews(view); 40 | initEvents(); 41 | } 42 | 43 | private void initViews(View view) { 44 | mGestureLockLayout = (GestureLockLayout) view.findViewById(R.id.l_lock_view); 45 | mHintText = (TextView) view.findViewById(R.id.tv_hint); 46 | //设置手势解锁模式为验证模式 47 | mGestureLockLayout.setMode(GestureLockLayout.VERIFY_MODE); 48 | //设置手势解锁每行每列点的个数 49 | mGestureLockLayout.setDotCount(3); 50 | //设置手势解锁最大尝试次数 默认 5 51 | mGestureLockLayout.setTryTimes(3); 52 | //设置手势解锁正确答案 53 | mGestureLockLayout.setAnswer(MyApplication.getInstance().answer); 54 | } 55 | 56 | private void initEvents() { 57 | mGestureLockLayout.setOnLockVerifyListener(new GestureLockLayout.OnLockVerifyListener() { 58 | @Override 59 | public void onGestureSelected(int id) { 60 | //每选中一个点时调用 61 | } 62 | 63 | @Override 64 | public void onGestureFinished(boolean isMatched) { 65 | //绘制手势解锁完成时调用 66 | 67 | if (isMatched) { 68 | //密码匹配 69 | MyApplication.getInstance().isUnlock = true; 70 | _mActivity.finish(); 71 | } else { 72 | //不匹配 73 | mHintText.setText("还有" + mGestureLockLayout.getTryTimes() + "次机会"); 74 | resetGesture(); 75 | } 76 | } 77 | 78 | @Override 79 | public void onGestureTryTimesBoundary() { 80 | //超出最大尝试次数时调用 81 | 82 | mGestureLockLayout.setTouchable(false); 83 | } 84 | }); 85 | } 86 | 87 | private void resetGesture() { 88 | mHandler.postDelayed(new Runnable() { 89 | @Override 90 | public void run() { 91 | mGestureLockLayout.resetGesture(); 92 | } 93 | }, 200); 94 | } 95 | 96 | @Override 97 | public void onDestroyView() { 98 | super.onDestroyView(); 99 | if (mHandler != null) { 100 | mHandler.removeCallbacksAndMessages(null); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/sample/LockSettingActivity.java: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.sample; 2 | 3 | import android.graphics.Color; 4 | import android.os.Bundle; 5 | import android.os.Handler; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.widget.TextView; 8 | 9 | import com.zyyoona7.lock.GestureLockDisplayView; 10 | import com.zyyoona7.lock.GestureLockLayout; 11 | import com.zyyoona7.lock.ILockView; 12 | import com.zyyoona7.lock.JDLockView; 13 | import com.zyyoona7.lock.LockViewFactory; 14 | 15 | import java.util.List; 16 | 17 | public class LockSettingActivity extends AppCompatActivity { 18 | 19 | private GestureLockLayout mGestureLockLayout; 20 | private GestureLockDisplayView mLockDisplayView; 21 | private TextView mSettingHintText; 22 | private Handler mHandler = new Handler(); 23 | 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_lock_setting); 28 | initViews(); 29 | initEvents(); 30 | } 31 | 32 | private void initViews() { 33 | mGestureLockLayout = (GestureLockLayout) findViewById(R.id.l_gesture_view); 34 | mLockDisplayView = (GestureLockDisplayView) findViewById(R.id.l_display_view); 35 | mSettingHintText = (TextView) findViewById(R.id.tv_setting_hint); 36 | //设置提示view 每行每列点的个数 37 | mLockDisplayView.setDotCount(3); 38 | //设置提示view 选中状态的颜色 39 | mLockDisplayView.setDotSelectedColor(Color.parseColor("#01A0E5")); 40 | //设置提示view 非选中状态的颜色 41 | mLockDisplayView.setDotUnSelectedColor(Color.TRANSPARENT); 42 | //设置手势解锁view 每行每列点的个数 43 | mGestureLockLayout.setDotCount(3); 44 | //设置手势解锁view 最少连接数 45 | mGestureLockLayout.setMinCount(3); 46 | //默认解锁样式为手Q手势解锁样式 47 | mGestureLockLayout.setLockView(new LockViewFactory() { 48 | @Override 49 | public ILockView newLockView() { 50 | return new JDLockView(LockSettingActivity.this); 51 | } 52 | }); 53 | //设置手势解锁view 模式为重置密码模式 54 | mGestureLockLayout.setMode(GestureLockLayout.RESET_MODE); 55 | } 56 | 57 | private void initEvents() { 58 | mGestureLockLayout.setOnLockResetListener(new GestureLockLayout.OnLockResetListener() { 59 | @Override 60 | public void onConnectCountUnmatched(int connectCount, int minCount) { 61 | //连接数小于最小连接数时调用 62 | 63 | mSettingHintText.setText("最少连接" + minCount + "个点"); 64 | resetGesture(); 65 | } 66 | 67 | @Override 68 | public void onFirstPasswordFinished(List answerList) { 69 | //第一次绘制手势成功时调用 70 | 71 | mSettingHintText.setText("确认解锁图案"); 72 | //将答案设置给提示view 73 | mLockDisplayView.setAnswer(answerList); 74 | //重置 75 | resetGesture(); 76 | } 77 | 78 | @Override 79 | public void onSetPasswordFinished(boolean isMatched, List answerList) { 80 | //第二次密码绘制成功时调用 81 | 82 | if (isMatched) { 83 | //两次答案一致,保存 84 | MyApplication.getInstance().answer = answerList.toString(); 85 | MyApplication.getInstance().isUnlock = false; 86 | finish(); 87 | } else { 88 | resetGesture(); 89 | } 90 | } 91 | }); 92 | } 93 | 94 | /** 95 | * 重置 96 | */ 97 | private void resetGesture() { 98 | mHandler.postDelayed(new Runnable() { 99 | @Override 100 | public void run() { 101 | mGestureLockLayout.resetGesture(); 102 | } 103 | }, 200); 104 | } 105 | 106 | @Override 107 | protected void onDestroy() { 108 | super.onDestroy(); 109 | if (mHandler != null) { 110 | mHandler.removeCallbacksAndMessages(null); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /app/src/main/java/com/zyyoona7/sample/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.zyyoona7.sample; 2 | 3 | import android.app.Application; 4 | 5 | /** 6 | * Created by zyyoona7 on 2017/7/10. 7 | */ 8 | 9 | public class MyApplication extends Application { 10 | 11 | public long currentTime=System.currentTimeMillis(); 12 | public String answer=""; 13 | public boolean isUnlock=false; 14 | private static MyApplication mInstance; 15 | 16 | @Override 17 | public void onCreate() { 18 | super.onCreate(); 19 | mInstance=this; 20 | } 21 | 22 | public static MyApplication getInstance(){ 23 | return mInstance; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/res/anim/cycle_4.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 20 | -------------------------------------------------------------------------------- /app/src/main/res/anim/fade_ins.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/anim/push_bottom_in_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/anim/shake.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 23 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 |