首页 > 系统 > Android > 正文

使用Android自定义控件实现滑动解锁九宫格

2019-10-24 20:35:40
字体:大 中 小
来源:转载
供稿:网友

最近由于Android项目需要,要求做一个类似于支付宝的九宫格解锁组件,下面小编给大家分享了具体实现代码,需要的朋友可以参考下

本文概述:

滑动解锁九宫格的分析:

1、需要自定义控件;

2、需要重写事件onTouchEvent();

3、需要给九个点设置序号和坐标,这里用Map类就行;

4、需要判断是否到滑到过九点之一,并存储滑到过的点的序号,而且需要一个方法可以返回它们,这里用List类就行;

滑动解锁当前还是比较流行的,今天写了个简单的滑动解锁九宫格的例程,分享出来让初学者看看。

我的是这样的:

使用Android自定义控件实现滑动解锁九宫格

Demo

首先,自定义一个View

 

 
  1. /** 
  2. * 九宫格 
  3. */ 
  4. public class NineGridView extends View { 
  5. private int width;//该控件的宽 
  6. private int height;//该控件的高 
  7. private Paint mPaintBigCircle;//用于画外圆 
  8. private Paint mPaintSmallCircle;//用于画内圆 
  9. private Paint mPaintLine;//用于画线 
  10. private Paint mPaintText;//用于画文本 
  11. private Path path;//手势划线时需要用到它 
  12. private Map<Integer, Float[]> pointContainer;//存储九个点的坐标 
  13. private List<Integer> pointerSlipped;//存储得到的九宫格密码 
  14. public List<Integer> getPointerSlipped() { 
  15. return pointerSlipped; 
  16. } 
  17. public void setPointerSlipped(List<Integer> pointerSlipped) { 
  18. this.pointerSlipped = pointerSlipped; 
  19. } 
  20. public NineGridView(Context context) { 
  21. super(context); 
  22. } 
  23. public NineGridView(Context context, AttributeSet attrs) { 
  24. super(context, attrs); 
  25. mPaintBigCircle = new Paint(); 
  26. mPaintBigCircle.setColor(Color.BLUE); 
  27. mPaintBigCircle.setStyle(Paint.Style.STROKE);//不充满 
  28. mPaintBigCircle.setAntiAlias(true);//抗锯齿打开 
  29. mPaintSmallCircle = new Paint(); 
  30. mPaintSmallCircle.setColor(Color.GREEN); 
  31. mPaintSmallCircle.setStyle(Paint.Style.FILL);//充满,即画的几何体为实心 
  32. mPaintSmallCircle.setAntiAlias(true); 
  33. mPaintLine = new Paint(); 
  34. mPaintLine.setColor(Color.GREEN); 
  35. mPaintLine.setStyle(Paint.Style.STROKE); 
  36. mPaintLine.setStrokeWidth(20); 
  37. mPaintLine.setAntiAlias(true); 
  38. mPaintText = new Paint(); 
  39. mPaintText.setColor(Color.WHITE); 
  40. mPaintText.setTextAlign(Paint.Align.CENTER);//向中央对齐 
  41. mPaintText.setTextSize(50); 
  42. mPaintText.setAntiAlias(true); 
  43. path = new Path(); 
  44. pointContainer = new HashMap<>(); 
  45. pointerSlipped = new ArrayList<>(); 
  46. } 
  47. @Override 
  48. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
  49. super.onMeasure(widthMeasureSpec, heightMeasureSpec); 
  50. width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec); 
  51. height = getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec); 
  52. } 
  53. private float pivotX;//触屏得到的x坐标 
  54. private float pivotY;//触屏得到的y坐标 
  55. private float selectedX;//当前选中的圆点的x坐标 
  56. private float selectedY;//当前选中的圆点的y坐标 
  57. private float selectedXOld;//从前选中的圆点的x坐标 
  58. private float selectedYOld;//从前选中的圆点的y坐标 
  59. private boolean isHasMoved = false;//用于判断path是否调用过moveTo()方法 
  60. @Override 
  61. public boolean onTouchEvent(MotionEvent event) { 
  62. switch (event.getAction()) { 
  63. case MotionEvent.ACTION_DOWN: 
  64. pivotX = event.getX(); 
  65. pivotY = event.getY(); 
  66. //每次触屏时需要清空一下pointerSlipped,即重置密码 
  67. pointerSlipped.clear(); 
  68. Log.d("pointTouched", pivotX + "," + pivotY); 
  69. getSelectedPointIndex(pivotX, pivotY); 
  70. invalidate();//重绘 
  71. break; 
  72. case MotionEvent.ACTION_MOVE: 
  73. pivotX = event.getX(); 
  74. pivotY = event.getY(); 
  75. getSelectedPointIndex(pivotX, pivotY); 
  76. invalidate(); 
  77. break; 
  78. case MotionEvent.ACTION_UP: 
  79. /** 
  80. * 当手指离开屏幕时,重置path 
  81. */ 
  82. path.reset(); 
  83. isHasMoved = false; 
  84. String indexSequence = ""; 
  85. //打印出上一次手势密码的值 
  86. for(int index:pointerSlipped){ 
  87. indexSequence += "/"+index; 
  88. } 
  89. Log.d("index",indexSequence); 
  90. break; 
  91. } 
  92. invalidate(); 
  93. return true; 
  94. } 
  95. /** 
  96. * 得到并存储经过的圆点的序号 
  97. * @param pivotX 
  98. * @param pivotY 
  99. */ 
  100. private void getSelectedPointIndex(float pivotX, float pivotY) { 
  101. int index = 0; 
  102. if (pivotX > patternMargin && pivotX < patternMargin + bigCircleRadius * 2) { 
  103. if (pivotY > height / 2 && pivotY < height / 2 + bigCircleRadius * 2) { 
  104. selectedX = pointContainer.get(1)[0]; 
  105. selectedY = pointContainer.get(1)[1]; 
  106. index = 1; 
  107. Log.d("selectedPoint", selectedX + "," + selectedY); 
  108. } else if (pivotY > height / 2 + added && pivotY < height / 2 + added + bigCircleRadius * 2) { 
  109. selectedX = pointContainer.get(4)[0]; 
  110. selectedY = pointContainer.get(4)[1]; 
  111. index = 4; 
  112. } else if (pivotY > height / 2 + added * 2 && pivotY < height / 2 + added * 2 + bigCircleRadius * 2) { 
  113. selectedX = pointContainer.get(7)[0]; 
  114. selectedY = pointContainer.get(7)[1]; 
  115. index = 7; 
  116. } 
  117. } else if (pivotX > patternMargin + added && pivotX < patternMargin + added + bigCircleRadius * 2) { 
  118. if (pivotY > height / 2 && pivotY < height / 2 + bigCircleRadius * 2) { 
  119. selectedX = pointContainer.get(2)[0]; 
  120. selectedY = pointContainer.get(2)[1]; 
  121. index = 2; 
  122. } else if (pivotY > height / 2 + added && pivotY < height / 2 + added + bigCircleRadius * 2) { 
  123. selectedX = pointContainer.get(5)[0]; 
  124. selectedY = pointContainer.get(5)[1]; 
  125. index = 5; 
  126. } else if (pivotY > height / 2 + added * 2 && pivotY <height / 2 + added * 2 + bigCircleRadius * 2) { 
  127. selectedX = pointContainer.get(8)[0]; 
  128. selectedY = pointContainer.get(8)[1]; 
  129. index = 8; 
  130. } 
  131. } else if (pivotX > patternMargin + added * 2 && pivotX < patternMargin + added * 2 + bigCircleRadius * 2) { 
  132. if (pivotY > height / 2 && pivotY < height / 2 + bigCircleRadius * 2) { 
  133. selectedX = pointContainer.get(3)[0]; 
  134. selectedY = pointContainer.get(3)[1]; 
  135. index = 3; 
  136. } else if (pivotY > height / 2 + added && pivotY < height / 2 + added + bigCircleRadius * 2) { 
  137. selectedX = pointContainer.get(6)[0]; 
  138. selectedY = pointContainer.get(6)[1]; 
  139. index = 6; 
  140. } else if (pivotY > height / 2 + added * 2 && pivotY < height / 2 + added * 2 + bigCircleRadius * 2) { 
  141. selectedX = pointContainer.get(9)[0]; 
  142. selectedY = pointContainer.get(9)[1]; 
  143. index = 9; 
  144. } 
  145. } 
  146. if (selectedX!=selectedXOld||selectedY!=selectedYOld){ 
  147. //当这次的坐标与上次的坐标不同时存储这次点序号 
  148. pointerSlipped.add(index); 
  149. selectedXOld = selectedX; 
  150. selectedYOld = selectedY; 
  151. if (!isHasMoved){ 
  152. //当第一次触碰到九个点之一时,path调用moveTo; 
  153. path.moveTo(selectedX,selectedY); 
  154. isHasMoved = true; 
  155. }else{ 
  156. //path移动至当前圆点坐标 
  157. path.lineTo(selectedX,selectedY); 
  158. } 
  159. } 
  160. } 
  161. private String text = "请绘制解锁图案"; 
  162. private float x;//绘制的圆形的x坐标 
  163. private float y;//绘制圆形的纵坐标 
  164. private float added;//水平竖直方向每个圆点中心间距 
  165. private float patternMargin = 100;//九宫格距离边界距离 
  166. private float bigCircleRadius = 90;//外圆半径 
  167. private float smallCircleRadius = 25;//内圆半径 
  168. private int index;//圆点的序号 
  169. @Override 
  170. protected void onDraw(Canvas canvas) { 
  171. super.onDraw(canvas); 
  172. added = (width - patternMargin * 2) / 3; 
  173. x = patternMargin + added / 2; 
  174. y = added / 2 + height / 2; 
  175. index = 1; 
  176. canvas.drawColor(Color.BLACK); 
  177. canvas.drawText(text, width / 2, height / 4, mPaintText); 
  178. /** 
  179. * 绘制九个圆点图案 
  180. */ 
  181. for (int column = 0; column < 3; column++) { 
  182. for (int row = 0; row < 3; row++) { 
  183. canvas.drawCircle(x, y, bigCircleRadius, mPaintBigCircle); 
  184. canvas.drawCircle(x, y, smallCircleRadius, mPaintSmallCircle); 
  185. pointContainer.put(index, new Float[]{x, y}); 
  186. index++; 
  187. x += added; 
  188. } 
  189. y += added; 
  190. x = patternMargin + added / 2; 
  191. } 
  192. x = patternMargin + added / 2; 
  193. y = added / 2 + height / 2; 
  194. canvas.drawPath(path, mPaintLine); 
  195. } 
  196. } 

为什么要规避重复?

因为在触屏时,会调用很多次onTouchEvent()方法,这样存储的手势密码肯定会不准确,我在以上代码中作出了处理,已经避免了重复,看打印信息:

这里写图片描述

显然,密码没有相邻数重复,当然还有一种情况就是手指在两个点之间来回等问题,这种状况也需要避免,这里没有作处理。当然,我做得还不够。。。

自定义view中用到的dp和px互相转换的工具类:

 

 
  1. public class SizeConvert { 
  2. /** 
  3. * 将dp转换为sp 
  4. */ 
  5. public static int dip2px(Context context, float dipValue){ 
  6. final float scale = context.getResources().getDisplayMetrics().density; 
  7. return (int)(dipValue * scale + 0.5f); 
  8. } 
  9. /** 
  10. * sp转dp 
  11. */ 
  12. public static int px2dip(Context context, float pxValue){ 
  13. final float scale = context.getResources().getDisplayMetrics().density; 
  14. return (int)(pxValue / scale + 0.5f); 
  15. } 
  16. } 

主活动:

 

 
  1. public class NineGridActivity extends BaseActivity{ 
  2. @Override 
  3. protected void onCreate(Bundle savedInstanceState) { 
  4. super.onCreate(savedInstanceState); 
  5. setContentView(R.layout.view_nine_grid); 
  6. } 
  7. } 

layout中的布局文件view_nine_grid:

 

 
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
  3. android:orientation="vertical" 
  4. android:layout_width="match_parent" 
  5. android:layout_height="match_parent"> 
  6. <com.test.shiweiwei.myproject.selfish_view.NineGridView 
  7. android:layout_width="match_parent" 
  8. android:layout_height="match_parent"/> 
  9. </LinearLayout> 

总结

我写的只是最基本的九宫格滑动解密项目,实际用的九宫格解密比这个要复杂,有许多特效和其他更严谨的处理,事件的处理也不是这样草草了事,如果想写得漂亮,还得多花工夫。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表