zoukankan      html  css  js  c++  java
  • WPF实现手势解锁

    桌面程序的解锁方式一般是账号密码,互联网的可以使用扫码解锁,甚至人脸识别。但扫码需要网络,人脸识别又较复杂。所以就想把安卓常用的手势解锁移植到桌面程序上。

    先来张效果图,有兴趣的往下看,没兴趣的打扰了。

     WPF手势解锁使用鼠标点击事件,鼠标移动事件,鼠标弹起事件实现。自定义了三个属性(初始化颜色,选中颜色,选中点的集合),一个事件(绘制完成后触发的事件)。

    实现的功能:

      绘制过程中直线随鼠标移动的效果

      绘制两个连接点的连线

      绘制完成后可调用的事件

      实现初始化颜色,选中颜色,选择连接点依赖属性

    源码主要说明:

    1.构造函数,完成事件注册

     1 /// <summary>
     2 /// 构造函数
     3 /// </summary>
     4 public ScreenUnlock()
     5 {
     6 InitializeComponent();
     7 Points = new List<int>();
     8 this.Loaded += ScreenUnlock_Loaded;
     9 this.MouseDown += ScreenUnlock_MouseDown;
    10 this.MouseUp += ScreenUnlock_MouseUp;
    11 this.MouseMove += ScreenUnlock_MouseMove;
    12 }
    View Code

    2.窗体加载事件

    绘制九宫格,tag用动态类型保存了实际位置(Point)和序号(Loaction)

     1 /// <summary>
     2 /// Load事件,绘制九宫格
     3 /// </summary>
     4 /// <param name="sender"></param>
     5 /// <param name="e"></param>
     6 private void ScreenUnlock_Loaded(object sender, RoutedEventArgs e)
     7 {
     8 canvas.Children.Clear();
     9 //为了保证正方形
    10 var distance = Math.Min(this.ActualWidth == 0 ? this.Width : this.ActualWidth, this.ActualHeight == 0 ? this.Height : this.ActualHeight) / 3;
    11 double left = (distance - PointSize) / 2;
    12 for (var i = 0; i < 3; i++)
    13 {
    14 for (var j = 0; j < 3; j++)
    15 {
    16 var x = j * distance + left;
    17 var y = i * distance + left;
    18 Ellipse ellipse = new Ellipse()
    19 {
    20 Width = PointSize,
    21 Height = PointSize,
    22 Fill = Color,
    23 Tag = new
    24 {
    25 Point = new Point(x + PointSize / 2, y + PointSize / 2),
    26 Location = i * 3 + j + 1
    27 }
    28 };
    29 ellipse.SetValue(Canvas.LeftProperty, x);
    30 ellipse.SetValue(Canvas.TopProperty, y);
    31 Canvas.SetLeft(ellipse, x);
    32 Canvas.SetTop(ellipse, y);
    33 canvas.Children.Add(ellipse);
    34 }
    35 }
    36 }
    View Code

    3.鼠标左键点击事件

    3.1清空了除九宫格之外所有元素

    3.2判断点击位置是否是圆点位置,如果不是则不处理,否则记录当前位置用于画线,一条是跟踪鼠标的线(currentLine),另一个是为了显示选中的圆点的连线,此处记录绘制线的第一个点(currentEllipse与后续经过的点的连线).

     1 private void ScreenUnlock_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
     2 {
     3 if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
     4 {
     5 //每次点击都是重新绘制,清空除了九宫格的所有元素
     6 while (canvas.Children.Count > 9)
     7 canvas.Children.RemoveAt(canvas.Children.Count - 1);
     8 ellipseList.Clear();
     9 currentEllipse = null;
    10 Points.Clear();
    11 
    12 //再次点击时需要先把颜色修改为初始化颜色
    13 foreach (Shape item in canvas.Children)
    14 item.Fill = Color;
    15 
    16 //获取当前鼠标位置
    17 var point = e.GetPosition(this);
    18 //鼠标所在位置是否有圆点
    19 if (VisualTreeHelper.HitTest(this, point).VisualHit is Ellipse ellipse) //鼠标经过圆点
    20 {
    21 currentEllipse = ellipse;
    22 ellipseList.Add(ellipse);
    23 Points.Add((int)((dynamic)ellipse.Tag).Location);
    24 var p = (Point)((dynamic)currentEllipse.Tag).Point;
    25 currentLine = new Line()
    26 {
    27 Stroke = Color,
    28 StrokeThickness = PointSize / 2,
    29 X1 = p.X,
    30 Y1 = p.Y,
    31 X2 = p.X,
    32 Y2 = p.Y
    33 };
    34 }
    35 }
    36 }
    View Code

    4.鼠标移动事件

    4.1绘制跟随鼠标的线

    4.2判断是否经过之前没经过的圆点,绘线过程中,一个点只能用一次。经过的点保存在ellipseList集合中。

    4.3如果经过未曾经过的点,则与上个经过的点绘制直线,并且重新赋值当前点currentEllipse,重新定义跟随鼠标的线(currentLine)的起始点为该点。

    4.4把点添加到Points集合中

     1 /// <summary>
     2 /// 鼠标移动
     3 /// </summary>
     4 /// <param name="sender"></param>
     5 /// <param name="e"></param>
     6 private void ScreenUnlock_MouseMove(object sender, System.Windows.Input.MouseEventArgs e)
     7 {
     8 //鼠标左键处于点击状态
     9 if (e.LeftButton == System.Windows.Input.MouseButtonState.Pressed)
    10 {
    11 //获取当前鼠标位置
    12 var point = e.GetPosition(this);
    13 
    14 ///当没有遇到圆点之前绘制跟随鼠标的线
    15 if (currentLine != null)
    16 {
    17 canvas.Children.Remove(currentLine);
    18 currentLine.X2 = point.X;
    19 currentLine.Y2 = point.Y;
    20 canvas.Children.Add(currentLine);
    21 }
    22 
    23 //线跟着移动
    24 if (VisualTreeHelper.HitTest(this, point).VisualHit is Ellipse ellipse && currentEllipse != null)
    25 {
    26 var p1 = (Point)((dynamic)currentEllipse.Tag).Point;
    27 var p = (Point)((dynamic)ellipse.Tag).Point;
    28 if (p1 != p) //鼠标经过圆点
    29 {
    30 //如果不包含该圆点,一个点只能用一次
    31 if (!ellipseList.Contains(ellipse))
    32 {
    33 //绘制当前点和上个点之间的连线
    34 var t = new Line()
    35 {
    36 Stroke = Color,
    37 StrokeThickness = PointSize / 2,
    38 X1 = p1.X,
    39 Y1 = p1.Y,
    40 X2 = p.X,
    41 Y2 = p.Y
    42 };
    43 //修改当前点
    44 currentEllipse = ellipse;
    45 ellipseList.Add(ellipse);
    46 canvas.Children.Add(t);
    47 Points.Add((int)((dynamic)ellipse.Tag).Location);
    48 if (currentLine != null)
    49 {
    50 canvas.Children.Remove(currentLine);
    51 currentLine.X1 = p.X;
    52 currentLine.Y1 = p.Y;
    53 currentLine.X2 = p.X;
    54 currentLine.Y2 = p.Y;
    55 canvas.Children.Add(currentLine);
    56 }
    57 }
    58 }
    59 }
    60 }
    61 }
    View Code

    5.鼠标左键弹起事件

    5.1鼠标弹起时,修改所有经过的点以及点之间的连线颜色。

    5.2清空所有使用的临时的变量,currentEllipse,currentLine

    5.3触发绘制后触发的事件(AfterDraw)

     1 /// <summary>
     2 /// 鼠标左键弹起
     3 /// </summary>
     4 /// <param name="sender"></param>
     5 /// <param name="e"></param>
     6 private void ScreenUnlock_MouseUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
     7 {
     8 if (canvas.Children.Count > 9)
     9 foreach (Shape item in canvas.Children)
    10 if (item is Line)
    11 item.Stroke = SelectedColor;
    12 else if (item is Ellipse ellipse && ellipseList.Contains(ellipse))
    13 item.Fill = SelectedColor;
    14 currentEllipse = null;
    15 currentLine = null;
    16 canvas.Children.Remove(currentLine);
    17 RaiseEvent(new RoutedEventArgs(AfterDrawEvent));
    18 }
    View Code

    6.选中点和线的颜色,SelectedColor属性

    设置该颜色时,需要同时修改选中的点和线的颜色。

     1 /// <summary>
     2 /// 选中的颜色
     3 /// </summary>
     4 public static readonly DependencyProperty SelectedColorProperty = DependencyProperty.Register("SelectedColor", typeof(SolidColorBrush), typeof(ScreenUnlock), new FrameworkPropertyMetadata(new SolidColorBrush(Colors.Green), new PropertyChangedCallback((s, e) =>
     5 {
     6 var t = s as ScreenUnlock;
     7 if (t.canvas.Children.Count > 9)
     8 for (int i = 9; i < t.canvas.Children.Count; i++)
     9 {
    10 Shape item = t.canvas.Children[i] as Shape;
    11 if (item is Line)
    12 item.Stroke = e.NewValue as SolidColorBrush;
    13 else if (item is Ellipse ellipse)
    14 item.Fill = e.NewValue as SolidColorBrush;
    15 }
    16 })));
    17 
    18 /// <summary>
    19 /// 选中的颜色
    20 /// </summary>
    21 public SolidColorBrush SelectedColor
    22 {
    23 get { return GetValue(SelectedColorProperty) as SolidColorBrush; }
    24 set { SetValue(SelectedColorProperty, value); }
    25 }
    View Code

    7.绘制用的点的集合Points

    绑定Points属性,后台就可以获取到绘制图案经历的点的集合。

    8.其他代码

    其他包含初始颜色,绘制完成以后触发的事件,以及使用到的变量。见源码。

    9.利用该控件实现解锁

    9.1绑定三个属性一个事件,分别是初始化颜色(Color),选中的颜色(SelectedColor),经过的点(Points)

    9.2SelectedColor绑定方式

    需要一个转换器(验证正确与否与颜色的转换),如果验证正确,则显示绿色,否则显示红色。

    9.3如果连接的点太少时,则需进行提示,并且恢复原来的状态(即把选中的颜色设置为初始化的颜色)

    参考:

    https://www.cnblogs.com/ShenNan/p/5587009.html

    源码:

    没找到上传附件,附上码云地址。

    https://gitee.com/yiyecao/temporary-components

  • 相关阅读:
    设计模式 --> (3)策略模式
    设计模式 --> (2)单例模式
    设计模式 --> (1)工厂模式
    Algorithm --> 十大排序算法
    Algorithm --> 求阶乘末尾0的个数
    网络通信 --> epoll用法
    数据库 --> sqlite3之api使用
    网络通信 --> select()用法
    网络通信 --> TCP三次握手和四次挥手
    『转载』周立功:专注的力量成就梦想
  • 原文地址:https://www.cnblogs.com/yiyecao/p/13627233.html
Copyright © 2011-2022 走看看