Xamarin.Android 手势密码
主要问题:
1. 图形绘制
- 实心圆
- 空心圆
- 线段
2. Touch事件处理
- MotionEventActions.Down
- MotionEventActions.Move
- MotionEventActions.Up
3. 各个圆坐标计算
4. 圆圈的选中逻辑
- 判断点是否在圆内
- 判断选中的两个圆心连线是否经过一个圆,如果是则该圆也是选中
1 using System; 2 using System.Collections.Generic; 3 using System.Linq; 4 using System.Text; 5 6 using Android.App; 7 using Android.Content; 8 using Android.Graphics; 9 using Android.OS; 10 using Android.Runtime; 11 using Android.Util; 12 using Android.Views; 13 using Android.Widget; 14 using static Android.Views.View; 15 using MyPassword.Droid.Utils; 16 using MyPassword.Utils; 17 18 namespace MyPassword.Droid.Views 19 { 20 public class GestureLockView : View,IOnTouchListener 21 { 22 23 24 private int Circle_R = 20; 25 private int Distance = 40; 26 private int Circle_r = 3; 27 28 private int Length = 0; 29 30 private double ViewWidth = 0; 31 private double ViewHight = 0; 32 private double MyPadding = 0; 33 34 private int X_Zero = 0; 35 private int Y_Zero = 0; 36 37 38 39 private List<Vector2> pointList = new List<Vector2>(); 40 41 private List<Vector2> checkedList = new List<Vector2>(); 42 43 private List<Vector2> drawList = new List<Vector2>(); 44 45 private List<int> indexList = new List<int>(); 46 47 Paint paint; 48 49 public delegate void CheckCompeleteDelegate(List<int> checkList); 50 51 private CheckCompeleteDelegate _CheckCompeleteDelegate; 52 53 public event CheckCompeleteDelegate CheckCompeleteEvent 54 { 55 add 56 { 57 _CheckCompeleteDelegate = Delegate.Combine(_CheckCompeleteDelegate, value) as CheckCompeleteDelegate; 58 } 59 remove 60 { 61 _CheckCompeleteDelegate = Delegate.Remove(_CheckCompeleteDelegate, value) as CheckCompeleteDelegate; 62 } 63 } 64 65 66 private void GetCheckedIndex() 67 { 68 indexList.Clear(); 69 foreach (var item in checkedList) 70 { 71 int index = pointList.IndexOf(item); 72 indexList.Add(index); 73 74 } 75 } 76 77 public GestureLockView(Context context, IAttributeSet attrs) : 78 base(context, attrs) 79 { 80 Initialize(); 81 } 82 83 public GestureLockView(Context context, IAttributeSet attrs, int defStyle) : 84 base(context, attrs, defStyle) 85 { 86 Initialize(); 87 } 88 89 private void Initialize() 90 { 91 Circle_r = DensityUtil.Dp2px(Context,3f); 92 Circle_R = DensityUtil.Dp2px(Context, 20f); 93 Distance = DensityUtil.Dp2px(Context, 40f); 94 Length = 3 * Circle_R * 2 + Distance * 2; 95 ViewWidth = DensityUtil.Dp2px(Context, 320f); 96 ViewHight = DensityUtil.Dp2px(Context, 320f); 97 98 MyPadding = (ViewWidth - Length) / 2; 99 X_Zero = (int)MyPadding + Circle_R; 100 Y_Zero = (int)MyPadding + Circle_R; 101 paint = new Paint(); 102 SetOnTouchListener(this); 103 InitPointList(); 104 PostInvalidate(); 105 } 106 107 108 109 110 protected override void OnDraw(Canvas canvas) 111 { 112 base.OnDraw(canvas); 113 114 int size = pointList.Count; 115 paint.StrokeWidth = 6; 116 paint.AntiAlias = true; 117 for (int i = 0; i < size; i++)//绘制元素点图 118 { 119 Vector2 item = pointList.ElementAt(i); 120 paint.Color = Color.Blue; 121 paint.SetStyle(Paint.Style.Fill);//设置为实心 122 canvas.DrawCircle(item.X, item.Y, Circle_r,paint); 123 paint.SetStyle(Paint.Style.Stroke);//设置为空心 124 canvas.DrawCircle(item.X, item.Y, Circle_R, paint); 125 } 126 size = drawList.Count; 127 for (int i = 0; i < size; i++)//绘制选中点图 128 { 129 Vector2 item = drawList.ElementAt(i); 130 paint.Color = Color.Red; 131 paint.SetStyle(Paint.Style.Fill);//设置为实心 132 canvas.DrawCircle(item.X, item.Y, Circle_r, paint); 133 if (i < size - 1) 134 { 135 Vector2 item2 = drawList.ElementAt(i + 1); 136 paint.Color = Color.Red; 137 canvas.DrawLine(item.X, item.Y, item2.X, item2.Y, paint); 138 paint.SetStyle(Paint.Style.Stroke);//设置为空心 139 canvas.DrawCircle(item.X, item.Y, Circle_R, paint); 140 } 141 } 142 143 } 144 145 146 public void Reset() 147 { 148 checkedList.Clear(); 149 drawList.Clear(); 150 PostInvalidate(); 151 } 152 153 154 private double touch_x = 0; 155 private double touch_y = 0; 156 157 public bool OnTouch(View v, MotionEvent e) 158 { 159 switch (e.Action) 160 { 161 case MotionEventActions.Down: 162 case MotionEventActions.Move: 163 touch_x = e.GetX(); 164 touch_y = e.GetY(); 165 ProcessTouchEvent(touch_x, touch_y); 166 break; 167 case MotionEventActions.Up: 168 GetCheckedIndex(); 169 if (_CheckCompeleteDelegate != null) 170 { 171 _CheckCompeleteDelegate.Invoke(indexList); 172 } 173 Reset(); 174 break; 175 } 176 return true; 177 } 178 179 180 private void ProcessTouchEvent(double x, double y) 181 { 182 if (x < 0 || y < 0 || x > ViewWidth || y > ViewHight) 183 { 184 185 } 186 else 187 { 188 Vector2 item = CheckRange(x, y, out bool isIn); 189 if (isIn && !IsAdded(item)) 190 { 191 if (checkedList.Count > 0) 192 { 193 var item2 = checkedList.Last(); 194 foreach (Vector2 v in pointList) 195 { 196 if (item != v && !IsAdded(v) && CheckOnLine(item, item2, v)) 197 { 198 checkedList.Add(v); 199 } 200 } 201 } 202 checkedList.Add(item); 203 } 204 else 205 { 206 drawList.Clear(); 207 drawList.AddRange(checkedList); 208 drawList.Add(item); 209 } 210 PostInvalidate(); 211 } 212 } 213 214 215 216 /// <summary> 217 /// 判断 v 是否在 v1、v2连线内 用了最粗暴的方法 218 /// </summary> 219 /// <param name="v1"></param> 220 /// <param name="v2"></param> 221 /// <param name="v"></param> 222 /// <returns></returns> 223 private bool CheckOnLine(Vector2 v1, Vector2 v2, Vector2 v) 224 { 225 double len = CalcLengthBetweenTwoPoint(v1, v2); 226 double len1 = CalcLengthBetweenTwoPoint(v1, v); 227 double len2 = CalcLengthBetweenTwoPoint(v2, v); 228 return len == len1 + len2; 229 } 230 231 /// <summary> 232 /// 计算v1、v2连线长度 233 /// </summary> 234 /// <param name="v1"></param> 235 /// <param name="v2"></param> 236 /// <returns></returns> 237 private double CalcLengthBetweenTwoPoint(Vector2 v1, Vector2 v2) 238 { 239 double value = Math.Pow(v1.X - v2.X, 2.0) + Math.Pow(v1.Y - v2.Y, 2.0); 240 241 return Math.Abs(Math.Sqrt(value)); 242 } 243 244 /// <summary> 245 /// 判断x、y 是否在其中一个圆内 246 /// </summary> 247 /// <param name="x"></param> 248 /// <param name="y"></param> 249 /// <param name="isIn"></param> 250 /// <returns></returns> 251 private Vector2 CheckRange(double x, double y, out bool isIn) 252 { 253 foreach (Vector2 v in pointList) 254 { 255 if (IsInCircle(x, y, v) && !IsAdded(v)) 256 { 257 isIn = true; 258 return v; 259 } 260 } 261 isIn = false; 262 return new Vector2 { X = (int)x, Y = (int)y }; 263 264 } 265 266 /// <summary> 267 /// 判断x、y 是否在 v 为圆心 Circle_R 为半径的圆内 268 /// </summary> 269 /// <param name="x"></param> 270 /// <param name="y"></param> 271 /// <param name="v"></param> 272 /// <returns></returns> 273 private bool IsInCircle(double x, double y, Vector2 v) 274 { 275 return Math.Pow(x - v.X, 2.0) + Math.Pow(y - v.Y, 2.0) <= Math.Pow(Circle_R, 2.0); 276 } 277 278 /// <summary> 279 /// 判断item 是否已经选中 280 /// </summary> 281 /// <param name="item"></param> 282 /// <returns></returns> 283 private bool IsAdded(Vector2 item) 284 { 285 return checkedList.Contains(item); 286 } 287 288 /// <summary> 289 /// 初始化 原始数据 290 /// </summary> 291 private void InitPointList() 292 { 293 //DebugUtil.WriteLine("Distance == "+ Distance); 294 int deta_x = 0; 295 int deta_y = 0; 296 int x = 0; 297 int y = 0; 298 for (int i = 0; i < 9; i++) 299 { 300 deta_x = i % 3; 301 deta_y = i / 3; 302 x = X_Zero + deta_x * (Distance + 2 * Circle_R); 303 y = Y_Zero + deta_y * (Distance + 2 * Circle_R); 304 305 306 pointList.Add(new Vector2 307 { 308 X = x, 309 Y = y 310 }); 311 312 } 313 } 314 315 } 316 317 class Vector2 { 318 public int X { get; set; } 319 public int Y { get; set; } 320 } 321 322 }