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

为什么要规避重复?

因为在触屏时,会调用很多次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. * sp转dp 
  10. */ 
  11. public static int px2dip(Context context, float pxValue){ 
  12. final float scale = context.getResources().getDisplayMetrics().density; 
  13. return (int)(pxValue / scale + 0.5f); 

主活动:

 

 
  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); 

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> 

总结

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

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