이 글에서는 주로 WPF 그래픽 잠금해제 컨트롤 ScreenUnLock을 사용하는 방법을 자세히 소개합니다. 관심 있는 친구들이 참고할 수 있습니다.
ScreenUnLock은 스마트폰에서도 동일한 패턴 잠금해제 기능을 제공합니다. 그래픽을 잠금 해제하거나 기억하는 목적은 그래픽을 그려서 달성됩니다.
휴대폰의 그래픽 잠금 해제 기능을 WPF로 이식해야겠다는 생각이 갑자기 들었습니다. 이는 회사의 프로젝트에도 적용되었습니다.
ScreenUnLock을 만들기 전에 먼저 그래픽 잠금 해제 구현 아이디어를 분석해 보겠습니다.
1. 9개 정사각형 그리드(또는 그 이상)의 원점을 생성하고 각 지점의 좌표값을 정의합니다
2. 호출자의 정의를 용이하게 하기 위해 그래픽 잠금 해제와 관련된 확장 속성 및 이벤트를 제공합니다. 예: 점과 선의 색상(Color), 작동 모드(Check|Remember), 확인을 위한 올바른 색상(RightColor), 실패한 확인을 위한 색상(ErrorColor), 잠금 해제 이벤트 OnCheckedPoint, 메모리 이벤트 OnRememberPoint 등
3 . MouseMove 이벤트 모니터링 선 그리기 동작을 정의합니다. 선화 부분도 이 글의 핵심입니다. 선 그리기 과정 중. 프로그램은 선이 그려지기 시작하는 지점과 통과하는 지점(이미 기록된 지점 제외)을 결정해야 합니다. 도면이 완성되었는지 등등.
4. 선 그리기 완료, 선 그리기 완료 동작은 동작 모드에 따라 처리됩니다. 그리고 관련된 맞춤 이벤트를 호출합니다
일반적인 아이디어는 위와 같으니 단계별로 ScreenUnLock 작성을 시작하겠습니다
Create ScreenUnLock
public partial class ScreenUnlock : UserControl
관련 속성 정의
/// <summary> /// 验证正确的颜色 /// </summary> private SolidColorBrush rightColor; /// <summary> /// 验证失败的颜色 /// </summary> private SolidColorBrush errorColor; /// <summary> /// 图案是否在检查中 /// </summary> private bool isChecking; public static readonly DependencyProperty PointArrayProperty = DependencyProperty.Register("PointArray", typeof(IList<string>), typeof(ScreenUnlock)); /// <summary> /// 记忆的坐标点 /// </summary> public IList<string> PointArray { get { return GetValue(PointArrayProperty) as IList<string>; } set { SetValue(PointArrayProperty, value); } } /// <summary> /// 当前坐标点集合 /// </summary> private IList<string> currentPointArray; /// <summary> /// 当前线集合 /// </summary> private IList<Line> currentLineList; /// <summary> /// 点集合 /// </summary> private IList<Ellipse> ellipseList; /// <summary> /// 当前正在绘制的线 /// </summary> private Line currentLine; public static readonly DependencyProperty OperationPorperty = DependencyProperty.Register("Operation", typeof(ScreenUnLockOperationType), typeof(ScreenUnlock), new FrameworkPropertyMetadata(ScreenUnLockOperationType.Remember)); /// <summary> /// 操作类型 /// </summary> public ScreenUnLockOperationType Operation { get { return (ScreenUnLockOperationType)GetValue(OperationPorperty); } set { SetValue(OperationPorperty, value); } } public static readonly DependencyProperty PointSizeProperty = DependencyProperty.Register("PointSize", typeof(double), typeof(ScreenUnlock), new FrameworkPropertyMetadata(15.0)); /// <summary> /// 坐标点大小 /// </summary> public double PointSize { get { return Convert.ToDouble(GetValue(PointSizeProperty)); } set { SetValue(PointSizeProperty, value); } } public static readonly DependencyProperty ColorProperty = DependencyProperty.Register("Color", typeof(SolidColorBrush), typeof(ScreenUnlock), new FrameworkPropertyMetadata(new SolidColorBrush(Colors.White), new PropertyChangedCallback((s, e) => { (s as ScreenUnlock).Refresh(); }))); /// <summary> /// 坐标点及线条颜色 /// </summary> public SolidColorBrush Color { get { return GetValue(ColorProperty) as SolidColorBrush; } set { SetValue(ColorProperty, value); } } /// <summary> /// 操作类型 /// </summary> public enum ScreenUnLockOperationType { Remember = 0, Check = 1 }
Initialize ScreenUnLock
public ScreenUnlock() { InitializeComponent(); this.Loaded += ScreenUnlock_Loaded; this.Unloaded += ScreenUnlock_Unloaded; this.MouseMove += ScreenUnlock_MouseMove; //监听绘制事件 } private void ScreenUnlock_Loaded(object sender, RoutedEventArgs e) { isChecking = false; rightColor = new SolidColorBrush(Colors.Green); errorColor = new SolidColorBrush(Colors.Red); currentPointArray = new List<string>(); currentLineList = new List<Line>(); ellipseList = new List<Ellipse>(); CreatePoint(); } private void ScreenUnlock_Unloaded(object sender, RoutedEventArgs e) { rightColor = null; errorColor = null; if (currentPointArray != null) this.currentPointArray.Clear(); if (currentLineList != null) this.currentLineList.Clear(); if (ellipseList != null) ellipseList.Clear(); this.canvasRoot.Children.Clear(); }
만들기 point
/// <summary> /// 创建点 /// </summary> private void CreatePoint() { canvasRoot.Children.Clear(); int row = 3, column = 3; //三行三列,九宫格 double oneColumnWidth = (this.ActualWidth == 0 ? this.Width : this.ActualWidth) / 3; //单列的宽度 double oneRowHeight = (this.ActualHeight == 0 ? this.Height : this.ActualHeight) / 3; //单列的高度 double leftDistance = (oneColumnWidth - PointSize) / 2; //单列左边距 double topDistance = (oneRowHeight - PointSize) / 2; //单列上边距 for (var i = 0; i < row; i++) { for (var j = 0; j < column; j++) { Ellipse ellipse = new Ellipse() { Width = PointSize, Height = PointSize, Fill = Color, Tag = string.Format("{0}{1}", i, j) }; Canvas.SetLeft(ellipse, j * oneColumnWidth + leftDistance); Canvas.SetTop(ellipse, i * oneRowHeight + topDistance); canvasRoot.Children.Add(ellipse); ellipseList.Add(ellipse); } } }
선 만들기
private Line CreateLine() { Line line = new Line() { Stroke = Color, StrokeThickness = 2 }; return line; }
점과 선이 생성 및 정의되고 그리기 이벤트를 들을 수 있습니다
private void ScreenUnlock_MouseMove(object sender, System.Windows.Input.MouseEventArgs e) { if (isChecking) //如果图形正在检查中,不响应后续处理 return; if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed) { var point = e.GetPosition(this); HitTestResult result = VisualTreeHelper.HitTest(this, point); Ellipse ellipse = result.VisualHit as Ellipse; if (ellipse != null) { if (currentLine == null) { //从头开始绘制 currentLine = CreateLine(); var ellipseCenterPoint = GetCenterPoint(ellipse); currentLine.X1 = currentLine.X2 = ellipseCenterPoint.X; currentLine.Y1 = currentLine.Y2 = ellipseCenterPoint.Y; currentPointArray.Add(ellipse.Tag.ToString()); Console.WriteLine(string.Join(",", currentPointArray)); currentLineList.Add(currentLine); canvasRoot.Children.Add(currentLine); } else { //遇到下一个点,排除已经经过的点 if (currentPointArray.Contains(ellipse.Tag.ToString())) return; OnAfterByPoint(ellipse); } } else if (currentLine != null) { //绘制过程中 currentLine.X2 = point.X; currentLine.Y2 = point.Y; //判断当前Line是否经过点 ellipse = IsOnLine(); if (ellipse != null) OnAfterByPoint(ellipse); } } else { if (currentPointArray.Count == 0) return; isChecking = true; if (currentLineList.Count + 1 != currentPointArray.Count) { //最后一条线的终点不在点上 //两点一线,点的个数-1等于线的条数 currentLineList.Remove(currentLine); //从已记录的线集合中删除最后一条多余的线 canvasRoot.Children.Remove(currentLine); //从界面上删除最后一条多余的线 currentLine = null; } if (Operation == ScreenUnLockOperationType.Check) { Console.WriteLine("playAnimation Check"); var result = CheckPoint(); //执行图形检查 //执行完成动画并触发检查事件 PlayAnimation(result, () => { if (OnCheckedPoint != null) { this.Dispatcher.BeginInvoke(OnCheckedPoint, this, new CheckPointArgs() { Result = result }); //触发检查完成事件 } }); } else if (Operation == ScreenUnLockOperationType.Remember) { Console.WriteLine("playAnimation Remember"); RememberPoint(); //记忆绘制的坐标 var args = new RememberPointArgs() { PointArray = this.PointArray }; //执行完成动画并触发记忆事件 PlayAnimation(true, () => { if (OnRememberPoint != null) { this.Dispatcher.BeginInvoke(OnRememberPoint, this, args); //触发图形记忆事件 } }); } } }
선이 가까운 지점을 통과하는지 확인
/// <summary> /// 两点计算一线的长度 /// </summary> /// <param name="pt1"></param> /// <param name="pt2"></param> /// <returns></returns> private double GetLineLength(double x1, double y1, double x2, double y2) { return Math.Sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); //根据两点计算线段长度公式 √((x1-x2)²x(y1-y2)²) } /// <summary> /// 判断线是否经过了某个点 /// </summary> /// <param name="ellipse"></param> /// <returns></returns> private Ellipse IsOnLine() { double lineAB = 0; //当前画线的长度 double lineCA = 0; //当前点和A点的距离 double lineCB = 0; //当前点和B点的距离 double dis = 0; double deciation = 1; //允许的偏差距离 lineAB = GetLineLength(currentLine.X1, currentLine.Y1, currentLine.X2, currentLine.Y2); //计算当前画线的长度 foreach (Ellipse ellipse in ellipseList) { if (currentPointArray.Contains(ellipse.Tag.ToString())) //排除已经经过的点 continue; var ellipseCenterPoint = GetCenterPoint(ellipse); //取当前点的中心点 lineCA = GetLineLength(currentLine.X1, currentLine.Y1, ellipseCenterPoint.X, ellipseCenterPoint.Y); //计算当前点到线A端的长度 lineCB = GetLineLength(currentLine.X2, currentLine.Y2, ellipseCenterPoint.X, ellipseCenterPoint.Y); //计算当前点到线B端的长度 dis = Math.Abs(lineAB - (lineCA + lineCB)); //线CA的长度+线CB的长度>当前线AB的长度 说明点不在线上 if (dis <= deciation) //因为绘制的点具有一个宽度和高度,所以需设定一个允许的偏差范围,让线靠近点就命中之(吸附效果) { return ellipse; } } return null; }
점이 맞는지 확인하고 배열순으로 하나씩 맞춰보세요
/// <summary> /// 检查坐标点是否正确 /// </summary> /// <returns></returns> private bool CheckPoint() { //PointArray:正确的坐标值数组 //currentPointArray:当前绘制的坐标值数组 if (currentPointArray.Count != PointArray.Count) return false; for (var i = 0; i < currentPointArray.Count; i++) { if (currentPointArray[i] != PointArray[i]) return false; } return true; }
합격점을 기록하고 새로운 라인을 만들어보세요
/// <summary> /// 记录经过的点 /// </summary> /// <param name="ellipse"></param> private void OnAfterByPoint(Ellipse ellipse) { var ellipseCenterPoint = GetCenterPoint(ellipse); currentLine.X2 = ellipseCenterPoint.X; currentLine.Y2 = ellipseCenterPoint.Y; currentLine = CreateLine(); currentLine.X1 = currentLine.X2 = ellipseCenterPoint.X; currentLine.Y1 = currentLine.Y2 = ellipseCenterPoint.Y; currentPointArray.Add(ellipse.Tag.ToString()); Console.WriteLine(string.Join(",", currentPointArray)); currentLineList.Add(currentLine); canvasRoot.Children.Add(currentLine); }
/// <summary> /// 获取原点的中心点坐标 /// </summary> /// <param name="ellipse"></param> /// <returns></returns> private Point GetCenterPoint(Ellipse ellipse) { Point p = new Point(Canvas.GetLeft(ellipse) + ellipse.Width / 2, Canvas.GetTop(ellipse) + ellipse.Height / 2); return p; }
그림이 완성되면, 완료 애니메이션을 실행하고 응답 모드 이벤트
/// <summary> /// 执行动画 /// </summary> /// <param name="result"></param> private void PlayAnimation(bool result, Action callback = null) { Task.Factory.StartNew(() => { this.Dispatcher.Invoke((Action)delegate { foreach (Line l in currentLineList) l.Stroke = result ? rightColor : errorColor; foreach (Ellipse e in ellipseList) if (currentPointArray.Contains(e.Tag.ToString())) e.Fill = result ? rightColor : errorColor; }); Thread.Sleep(1500); this.Dispatcher.Invoke((Action)delegate { foreach (Line l in currentLineList) this.canvasRoot.Children.Remove(l); foreach (Ellipse e in ellipseList) e.Fill = Color; }); currentLine = null; this.currentPointArray.Clear(); this.currentLineList.Clear(); isChecking = false; }).ContinueWith(t => { try { if (callback != null) callback(); } catch (Exception ex) { Console.WriteLine(ex.Message); } finally { t.Dispose(); } }); }
Call of Graphics Unlock
<local:ScreenUnlock Width="500" Height="500" PointArray="{Binding PointArray, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Operation="Check"> <!--或Remember--> <i:Interaction.Triggers> <i:EventTrigger EventName="OnCheckedPoint"> <Custom:EventToCommand Command="{Binding OnCheckedPoint}" PassEventArgsToCommand="True"/> </i:EventTrigger> <i:EventTrigger EventName="OnRememberPoint"> <Custom:EventToCommand Command="{Binding OnRememberPoint}" PassEventArgsToCommand="True"/> </i:EventTrigger> </i:Interaction.Triggers> </local:ScreenUnlock>
위 내용은 WPF 그래픽을 사용하여 ScreenUnLock 컨트롤을 잠금 해제하는 방법에 대한 자세한 설명의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!