Xamarin.ios 手势密码
主要问题:
1. 图形绘制
- 实心圆
- 空心圆
- 线段
2. Touch事件处理
- TouchesBegan
- TouchesMoved
- TouchesEnded
- TouchesCancelled
3. 各个圆坐标计算
4. 圆圈的选中逻辑
- 判断点是否在圆内
- 判断选中的两个圆心连线是否经过一个圆,如果是则该圆也是选中
using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using CoreGraphics; using Foundation; using ObjCRuntime; using UIKit; namespace MyPassword.iOS.View.GuestureLock { [Register("GuestureLockUIView")] public partial class GuestureLockUIView : UIView { private int Circle_R = 20; private int Distance = 40; private int Circle_r = 3; private int Length = 0; private double ViewWidth = 0; private double ViewHight = 0; private double MyPadding = 0; private int X_Zero = 0; private int Y_Zero = 0; CGContext cGContext; public GuestureLockUIView(IntPtr handle) : base(handle) { Initialize(); } public GuestureLockUIView() { Initialize(); } public GuestureLockUIView(RectangleF bounds) : base(bounds) { Initialize(); } void Initialize() { BackgroundColor = UIColor.Green; Length = 3 * Circle_R * 2 + Distance * 2; ViewWidth = 320f; ViewHight = 320f; MyPadding = (ViewWidth - Length) / 2; X_Zero = (int)MyPadding + Circle_R; Y_Zero = (int)MyPadding + Circle_R; InitPointList(); } private List<Vector2> pointList = new List<Vector2>(); private List<Vector2> checkedList = new List<Vector2>(); private List<Vector2> drawList = new List<Vector2>(); private List<int> indexList = new List<int>(); public delegate void CheckCompeleteDelegate(List<int> checkList); private CheckCompeleteDelegate _CheckCompeleteDelegate; public event CheckCompeleteDelegate CheckCompeleteEvent { add { _CheckCompeleteDelegate = Delegate.Combine(_CheckCompeleteDelegate, value) as CheckCompeleteDelegate; } remove { _CheckCompeleteDelegate = Delegate.Remove(_CheckCompeleteDelegate, value) as CheckCompeleteDelegate; } } private void GetCheckedIndex() { indexList.Clear(); foreach (var item in checkedList) { int index = pointList.IndexOf(item); indexList.Add(index); } } public override void Draw(CGRect rect) { base.Draw(rect); cGContext = UIGraphics.GetCurrentContext(); int size = pointList.Count; for (int i = 0; i < size; i++)//绘制元素点图 { Vector2 item = pointList.ElementAt(i); cGContext.SetFillColor(UIColor.Blue.CGColor); cGContext.AddEllipseInRect(new CGRect(item.X - Circle_r, item.Y - Circle_r, Circle_r * 2, Circle_r * 2)); cGContext.DrawPath(CGPathDrawingMode.Fill); cGContext.SetStrokeColor(UIColor.Blue.CGColor); cGContext.SetLineWidth(2); cGContext.AddEllipseInRect(new CGRect(item.X- Circle_R, item.Y- Circle_R, Circle_R * 2, Circle_R * 2)); cGContext.DrawPath(CGPathDrawingMode.Stroke); } size = drawList.Count; for (int i = 0; i < size; i++)//绘制选中点图 { Vector2 item = drawList.ElementAt(i); cGContext.SetFillColor(UIColor.Red.CGColor); cGContext.AddEllipseInRect(new CGRect(item.X- Circle_r, item.Y- Circle_r, Circle_r * 2, Circle_r * 2)); cGContext.DrawPath(CGPathDrawingMode.Fill); if (i < size - 1) { Vector2 item2 = drawList.ElementAt(i + 1); cGContext.SetStrokeColor(UIColor.Red.CGColor); cGContext.MoveTo(item.X,item.Y); cGContext.AddLineToPoint(item2.X,item2.Y); cGContext.DrawPath(CGPathDrawingMode.Stroke); cGContext.SetStrokeColor(UIColor.Red.CGColor); cGContext.SetLineWidth(2); cGContext.AddEllipseInRect(new CGRect(item.X - Circle_R, item.Y- Circle_R, Circle_R * 2, Circle_R * 2)); cGContext.DrawPath(CGPathDrawingMode.Stroke); } } } public void Reset() { checkedList.Clear(); drawList.Clear(); SetNeedsDisplay(); } private double touch_x = 0; private double touch_y = 0; public override void TouchesBegan(NSSet touches, UIEvent evt) { base.TouchesBegan(touches, evt); if (touches.AnyObject is UITouch touch) { touch_x = touch.LocationInView(this).X; touch_y = touch.LocationInView(this).Y; ProcessTouchEvent(touch_x, touch_y); } } public override void TouchesMoved(NSSet touches, UIEvent evt) { base.TouchesMoved(touches, evt); if (touches.AnyObject is UITouch touch) { touch_x= touch.LocationInView(this).X; touch_y= touch.LocationInView(this).Y; ProcessTouchEvent(touch_x, touch_y); } } public override void TouchesEnded(NSSet touches, UIEvent evt) { base.TouchesEnded(touches, evt); GetCheckedIndex(); if (_CheckCompeleteDelegate != null) { _CheckCompeleteDelegate.Invoke(indexList); } Reset(); } public override void TouchesCancelled(NSSet touches, UIEvent evt) { base.TouchesEnded(touches, evt); Reset(); } private void ProcessTouchEvent(double x, double y) { //DebugUtil.WriteLine("x = "+x + " y = "+y); if (x < 0 || y < 0 || x > ViewWidth || y > ViewHight) { } else { Vector2 item = CheckRange(x, y, out bool isIn); if (isIn && !IsAdded(item)) { if (checkedList.Count > 0) { var item2 = checkedList.Last(); foreach (Vector2 v in pointList) { if (item != v && !IsAdded(v) && CheckOnLine(item, item2, v)) { checkedList.Add(v); } } } checkedList.Add(item); } else { drawList.Clear(); drawList.AddRange(checkedList); drawList.Add(item); } SetNeedsDisplay(); } } /// <summary> /// 判断 v 是否在 v1、v2连线内 用了最粗暴的方法 /// </summary> /// <param name="v1"></param> /// <param name="v2"></param> /// <param name="v"></param> /// <returns></returns> private bool CheckOnLine(Vector2 v1, Vector2 v2, Vector2 v) { double len = CalcLengthBetweenTwoPoint(v1, v2); double len1 = CalcLengthBetweenTwoPoint(v1, v); double len2 = CalcLengthBetweenTwoPoint(v2, v); return len == len1 + len2; } /// <summary> /// 计算v1、v2连线长度 /// </summary> /// <param name="v1"></param> /// <param name="v2"></param> /// <returns></returns> private double CalcLengthBetweenTwoPoint(Vector2 v1, Vector2 v2) { double value = Math.Pow(v1.X - v2.X, 2.0) + Math.Pow(v1.Y - v2.Y, 2.0); //return value; return Math.Abs(Math.Sqrt(value)); } /// <summary> /// 判断x、y 是否在其中一个圆内 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="isIn"></param> /// <returns></returns> private Vector2 CheckRange(double x, double y, out bool isIn) { foreach (Vector2 v in pointList) { if (IsInCircle(x, y, v) && !IsAdded(v)) { isIn = true; return v; } } isIn = false; return new Vector2 { X = (int)x, Y = (int)y }; } /// <summary> /// 判断x、y 是否在 v 为圆心 Circle_R 为半径的圆内 /// </summary> /// <param name="x"></param> /// <param name="y"></param> /// <param name="v"></param> /// <returns></returns> private bool IsInCircle(double x, double y, Vector2 v) { return Math.Pow(x - v.X, 2.0) + Math.Pow(y - v.Y, 2.0) <= Math.Pow(Circle_R, 2.0); } /// <summary> /// 判断item 是否已经选中 /// </summary> /// <param name="item"></param> /// <returns></returns> private bool IsAdded(Vector2 item) { return checkedList.Contains(item); } /// <summary> /// 初始化 原始数据 /// </summary> private void InitPointList() { int deta_x = 0; int deta_y = 0; int x = 0; int y = 0; for (int i = 0; i < 9; i++) { deta_x = i % 3; deta_y = i / 3; x = X_Zero + deta_x * (Distance + 2 * Circle_R); y = Y_Zero + deta_y * (Distance + 2 * Circle_R); pointList.Add(new Vector2 { X = x, Y = y }); } } } class Vector2 { public int X { get; set; } public int Y { get; set; } } }