UWP 手势密码实现
通过在画布canvas上绘制实现手势密码:
主要问题:
1. 图形绘制
- 实心圆
- 空心圆
- 线段
2. Touch事件处理
- ManipulationStarted
- ManipulationDelta
- ManipulationCompleted
3. 各个圆坐标计算
4. 圆圈的选中逻辑
- 判断点是否在圆内
- 判断选中的两个圆心连线是否经过一个圆,如果是则该圆也是选中
Xaml布局:
1<UserControl2x:Class="MyPassword.UWP.Control.GesturePassword"3xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"4xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"5xmlns:local="using:MyPassword.UWP.Control"6xmlns:d="http://schemas.microsoft.com/expression/blend/2008"7xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"8mc:Ignorable="d"9xmlns:canvas="using:Microsoft.Graphics.Canvas.UI.Xaml"10d:DesignHeight="300"11d:DesignWidth="400">1213<GridBackground="Transparent">14<canvas:CanvasControlx:Name="canvas"Draw="Canvas_Draw"Loaded="Canvas_Loaded"></canvas:CanvasControl>15</Grid>16</UserControl>
C#
1 using MyPassword.Utils; 2 using System; 3 using System.Collections.Generic; 4 using System.IO; 5 using System.Linq; 6 using System.Numerics; 7 using System.Runtime.InteropServices.WindowsRuntime; 8 using Windows.Foundation; 9 using Windows.Foundation.Collections; 10 using Windows.UI; 11 using Windows.UI.Xaml; 12 using Windows.UI.Xaml.Controls; 13 using Windows.UI.Xaml.Controls.Primitives; 14 using Windows.UI.Xaml.Data; 15 using Windows.UI.Xaml.Input; 16 using Windows.UI.Xaml.Media; 17 using Windows.UI.Xaml.Navigation; 18 19 // The User Control item template is documented at https://go.microsoft.com/fwlink/?LinkId=234236 20 21 namespace MyPassword.UWP.Control 22 { 23 public sealed partial class GesturePassword : UserControl 24 { 25 26 private const int Circle_R = 20; 27 private const int Distance = 40; 28 private const int Circle_r = 3; 29 30 private const int Length = 3 * Circle_R * 2 + Distance * 2; 31 32 private double ViewWidth = 0; 33 private double ViewHight = 0; 34 private double MyPadding = 0; 35 36 private int X_Zero = 0; 37 private int Y_Zero = 0; 38 39 40 private List<Vector2> pointList = new List<Vector2>(); 41 42 private List<Vector2> checkedList = new List<Vector2>(); 43 44 private List<Vector2> drawList = new List<Vector2>(); 45 46 private List<int> indexList = new List<int>(); 47 48 public delegate void CheckCompeleteDelegate(List<int> checkList); 49 50 private CheckCompeleteDelegate _CheckCompeleteDelegate; 51 52 public event CheckCompeleteDelegate CheckCompeleteEvent 53 { 54 add 55 { 56 _CheckCompeleteDelegate = Delegate.Combine(_CheckCompeleteDelegate, value) as CheckCompeleteDelegate; 57 } 58 remove 59 { 60 _CheckCompeleteDelegate = Delegate.Remove(_CheckCompeleteDelegate, value) as CheckCompeleteDelegate; 61 } 62 } 63 64 public GesturePassword() 65 { 66 this.InitializeComponent(); 67 Loaded += GesturePassword_Loaded; 68 Unloaded += GesturePassword_Unloaded; 69 } 70 71 72 73 74 private void GesturePassword_Loaded(object sender, RoutedEventArgs e) 75 { 76 canvas.ManipulationMode = ManipulationModes.TranslateX | ManipulationModes.TranslateY; 77 canvas.ManipulationCompleted += Canvas_ManipulationCompleted; 78 canvas.ManipulationDelta += Canvas_ManipulationDelta; 79 canvas.ManipulationStarted += Canvas_ManipulationStarted; 80 } 81 82 83 84 private double touch_x = 0; 85 private double touch_y = 0; 86 87 private void Canvas_ManipulationStarted(object sender, ManipulationStartedRoutedEventArgs e) 88 { 89 touch_x = e.Position.X; 90 touch_y = e.Position.Y; 91 ProcessTouchEvent(touch_x, touch_y); 92 } 93 94 private void Canvas_ManipulationDelta(object sender, ManipulationDeltaRoutedEventArgs e) 95 { 96 touch_x = e.Position.X; 97 touch_y = e.Position.Y; 98 ProcessTouchEvent(touch_x,touch_y); 99 } 100 101 private void Canvas_ManipulationCompleted(object sender, ManipulationCompletedRoutedEventArgs e) 102 { 103 104 GetCheckedIndex(); 105 if (_CheckCompeleteDelegate != null) 106 { 107 _CheckCompeleteDelegate.Invoke(indexList); 108 } 109 Reset(); 110 } 111 112 private void GetCheckedIndex() 113 { 114 indexList.Clear(); 115 //DebugUtil.WriteLine("Count == " + checkedList.Count); 116 foreach (var item in checkedList) 117 { 118 int index = pointList.IndexOf(item); 119 indexList.Add(index); 120 //DebugUtil.WriteLine("index == " +index); 121 } 122 //DebugUtil.WriteLine("Size == " + indexList.Count); 123 } 124 125 126 private void ProcessTouchEvent(double x,double y) 127 { 128 if (x < 0 || y < 0 || x > 320 || y > 320) 129 { 130 131 } 132 else 133 { 134 Vector2 item = CheckRange(x, y, out bool isIn); 135 if (isIn && !IsAdded(item)) 136 { 137 if (checkedList.Count > 0) 138 { 139 var item2 = checkedList.Last(); 140 foreach (Vector2 v in pointList) 141 { 142 if (item != v && !IsAdded(v) && CheckOnLine(item, item2, v)) 143 { 144 checkedList.Add(v); 145 } 146 } 147 } 148 checkedList.Add(item); 149 } 150 else 151 { 152 drawList.Clear(); 153 drawList.AddRange(checkedList); 154 drawList.Add(item); 155 } 156 canvas.Invalidate(); 157 } 158 } 159 160 /// <summary> 161 /// 判断 v 是否在 v1、v2连线内 用了最粗暴的方法 162 /// </summary> 163 /// <param name="v1"></param> 164 /// <param name="v2"></param> 165 /// <param name="v"></param> 166 /// <returns></returns> 167 private bool CheckOnLine(Vector2 v1,Vector2 v2,Vector2 v) 168 { 169 double len = CalcLengthBetweenTwoPoint(v1,v2); 170 double len1 = CalcLengthBetweenTwoPoint(v1, v); 171 double len2 = CalcLengthBetweenTwoPoint(v2, v); 172 return len == len1 + len2; 173 } 174 175 /// <summary> 176 /// 计算v1、v2连线长度 177 /// </summary> 178 /// <param name="v1"></param> 179 /// <param name="v2"></param> 180 /// <returns></returns> 181 private double CalcLengthBetweenTwoPoint(Vector2 v1, Vector2 v2) 182 { 183 double value = Math.Pow(v1.X - v2.X, 2.0) + Math.Pow(v1.Y - v2.Y, 2.0); 184 //return value; 185 return Math.Abs(Math.Sqrt(value)); 186 } 187 188 /// <summary> 189 /// 判断x、y 是否在其中一个圆内 190 /// </summary> 191 /// <param name="x"></param> 192 /// <param name="y"></param> 193 /// <param name="isIn"></param> 194 /// <returns></returns> 195 private Vector2 CheckRange(double x ,double y,out bool isIn) 196 { 197 foreach (Vector2 v in pointList) 198 { 199 if (IsInCircle(x, y, v) && !IsAdded(v)) 200 { 201 isIn = true; 202 return v; 203 } 204 } 205 isIn = false; 206 return new Vector2 { X = (int)x,Y = (int)y}; 207 208 } 209 210 /// <summary> 211 /// 判断x、y 是否在 v 为圆心 Circle_R 为半径的圆内 212 /// </summary> 213 /// <param name="x"></param> 214 /// <param name="y"></param> 215 /// <param name="v"></param> 216 /// <returns></returns> 217 private bool IsInCircle(double x, double y,Vector2 v) 218 { 219 return Math.Pow(x - v.X, 2.0) + Math.Pow(y - v.Y, 2.0) <= Math.Pow(Circle_R, 2.0); 220 } 221 222 /// <summary> 223 /// 判断item 是否已经选中 224 /// </summary> 225 /// <param name="item"></param> 226 /// <returns></returns> 227 private bool IsAdded(Vector2 item) 228 { 229 return checkedList.Contains(item); 230 } 231 232 /// <summary> 233 /// 初始化 原始数据 234 /// </summary> 235 private void InitPointList() 236 { 237 int deta_x = 0; 238 int deta_y = 0; 239 int x = 0; 240 int y = 0; 241 for (int i = 0; i < 9; i++) 242 { 243 deta_x = i % 3; 244 deta_y = i / 3; 245 x = X_Zero + deta_x * (Distance + 2 * Circle_R); 246 y = Y_Zero + deta_y * (Distance + 2 * Circle_R); 247 248 pointList.Add(new Vector2 249 { 250 X = x, 251 Y = y 252 }); 253 254 } 255 } 256 257 258 private void Canvas_Draw(Microsoft.Graphics.Canvas.UI.Xaml.CanvasControl sender,Microsoft.Graphics.Canvas.UI.Xaml.CanvasDrawEventArgs args) 259 { 260 261 int size = pointList.Count; 262 for (int i = 0; i < size; i++)//绘制元素点图 263 { 264 Vector2 item = pointList.ElementAt(i); 265 args.DrawingSession.DrawCircle(item,Circle_R, Colors.Blue,3); 266 args.DrawingSession.DrawCircle(item, 0, Colors.Blue, 6); 267 } 268 size = drawList.Count; 269 for (int i = 0; i < size; i++)//绘制选中点图 270 { 271 Vector2 item = drawList.ElementAt(i); 272 args.DrawingSession.DrawCircle(item, 0, Colors.Red, 6); 273 if (i < size - 1) 274 { 275 Vector2 item2 = drawList.ElementAt(i+1); 276 args.DrawingSession.DrawLine(item, item2, Colors.Red, 3); 277 args.DrawingSession.DrawCircle(item, Circle_R, Colors.Red, 3); 278 } 279 } 280 } 281 282 private void GesturePassword_Unloaded(object sender, RoutedEventArgs e) 283 { 284 canvas.RemoveFromVisualTree(); 285 canvas = null; 286 } 287 288 private void Canvas_Loaded(object sender, RoutedEventArgs e) 289 { 290 ViewWidth = canvas.ActualWidth; 291 ViewHight = canvas.ActualHeight; 292 MyPadding = (ViewWidth - Length) / 2; 293 X_Zero = (int)MyPadding + Circle_R; 294 Y_Zero = (int)MyPadding + Circle_R; 295 InitPointList(); 296 } 297 298 public void Reset() 299 { 300 checkedList.Clear(); 301 drawList.Clear(); 302 canvas.Invalidate(); 303 } 304 } 305 }