Android风格的登录屏幕模式






4.93/5 (12投票s)
绘制图案代替密码用于登录屏幕。
引言
本文解释了如何创建智能手机风格的密码屏幕。身份验证基于用户名和屏幕上绘制的屏幕代码。
背景
这个基本想法来自基于 Android 的智能手机,它们提供基于屏幕上绘制的图案的屏幕锁定/解锁功能。
使用代码
登录过程
在登录屏幕中,将有两个身份验证过程。 将对用户名进行身份验证,并显示图案画布。 绘制图案并在完成后,将对用户进行身份验证。 完成用户名身份验证是为了检索输入用户的屏幕图案信息。
屏幕要求
在用户名身份验证后显示屏幕图案画布。 通过选择隐身模式,可以在可见/不可见模式下绘制图案。 如果输入无效,屏幕代码将以红色显示一秒钟,然后自动擦除。 成功后,将显示已通过身份验证的消息,并且屏幕代码将被擦除。 允许在身份验证成功后编辑屏幕代码。 屏幕代码可以保存,并且内部绘制的图像将通过邮件发送给用户,绘制的图案将在图像区域中供用户参考。
如何实现以上要求
第一部分
- 创建一个
LockView
用户控件。 - 创建图案
Canvas
。 - 创建一个带有标签值设置(1,2..9)的椭圆 3x3 矩阵。
- 注册椭圆 Mouse Down 事件。
- 注册椭圆 Mouse Move 事件。
- 注册
Canvas
Mouse up 事件。 - 创建隐身模式复选框。
- 注册保存和清除按钮事件。
第二部分
- 创建验证委托,参数包括屏幕数据和验证标志。
- 创建保存委托,参数包括屏幕数据。
- 创建屏幕数据,包括验证委托对象、保存委托对象、屏幕代码、目标屏幕代码、绘制的图像源。
第三部分
- 创建锁基类,其中屏幕数据和屏幕模式作为依赖属性,以及绘制图案、插入图案数据、验证图案、保存图案、将屏幕图案捕获为图像源的方法。
- 使用 MD5Hash 加密屏幕图案并设置屏幕代码值。
如何绘制图案
在椭圆鼠标按下时创建一个折线形状,并在鼠标在有效椭圆上移动时继续添加点。 添加带有箭头的线条以显示图案的流动方向。 箭头线是根据折线点集合中的当前点和前一个点创建的。 应用 theta 来计算箭头角度。
NewLine = new Polyline();
NewLine.Name = CodeLine;
NewLine.Stroke = color;
NewLine.StrokeThickness = 10.0;
/// <summary>
/// Draw arrow lines for the selected points
/// </summary>
/// <param name="p1"></param>
/// <param name="p2"></param>
/// <param name="arrowName"></param>
/// <returns></returns>
protected static Shape DrawLinkArrow(Point p1, Point p2, string arrowName)
{
GeometryGroup lineGroup = new GeometryGroup();
double theta = Math.Atan2((p2.Y - p1.Y), (p2.X - p1.X)) * 180 / Math.PI;
PathGeometry pathGeometry = new PathGeometry();
PathFigure pathFigure = new PathFigure();
Point p = new Point(p1.X + ((p2.X - p1.X) / 1.35), p1.Y + ((p2.Y - p1.Y) / 1.35));
pathFigure.StartPoint = p;
Point lpoint = new Point(p.X + 6, p.Y + 15);
Point rpoint = new Point(p.X - 6, p.Y + 15);
LineSegment seg1 = new LineSegment();
seg1.Point = lpoint;
pathFigure.Segments.Add(seg1);
LineSegment seg2 = new LineSegment();
seg2.Point = rpoint;
pathFigure.Segments.Add(seg2);
LineSegment seg3 = new LineSegment();
seg3.Point = p;
pathFigure.Segments.Add(seg3);
pathGeometry.Figures.Add(pathFigure);
RotateTransform transform = new RotateTransform();
transform.Angle = theta + 90;
transform.CenterX = p.X;
transform.CenterY = p.Y;
pathGeometry.Transform = transform;
lineGroup.Children.Add(pathGeometry);
LineGeometry connectorGeometry = new LineGeometry();
connectorGeometry.StartPoint = p1;
connectorGeometry.EndPoint = p2;
lineGroup.Children.Add(connectorGeometry);
System.Windows.Shapes.Path path = new System.Windows.Shapes.Path();
path.Data = lineGroup;
path.StrokeThickness = 2;
path.Stroke = path.Fill = Brushes.Black;
path.Name = string.Format("ArrowLine{0}", arrowName);
return path;
}
检查重复条目并将每个椭圆的标签值插入堆栈。 在鼠标抬起时,通过将其加密为 MD5hash 字符串来验证图案与目标屏幕代码是否匹配。 使用验证标志和屏幕数据作为参数调用 onvalidate
委托。 启动一个时间间隔为 1 秒的计时器,以清除屏幕上的所有代码和图案。 如果屏幕图案无效,请用红色重新绘制图案并再次启动计时器以清除屏幕。
在画布中创建一个标签值从 (1,2,3...9) 开始的椭圆 3x3 矩阵
XAML 代码
<Canvas x:Name="LockCanvas" MouseUp="Canvas_MouseUp" Margin="110,20,80,10"
HorizontalAlignment="Center" Height="180" Width="200"
Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2">
<Ellipse x:Name="Code1" Tag="1" Margin="5" Canvas.Left="0"
Canvas.Top="0" Fill="Silver" Height="35"
Width="35"
StrokeThickness="5"
Stroke="Black" MouseMove="Canvas_MouseMove"
MouseDown="Canvas_MouseDown"/>
:
:
</Canvas>
C# 代码
//Draw pattern on mouse down
CodeSequence = new Stack<int>();
ClearAll();
CreatePolyLine(Brushes.Green);
FillCodeSequence(sender as Ellipse);
if (!chkStealthMode.IsChecked.Value)
{
FillCodeColor(sender as Ellipse, Brushes.Green);
LockCanvas.Children.Add(NewLine);
}
//start drawing the screen code on mouse move checking duplicity and starting and end points
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (e.LeftButton == MouseButtonState.Pressed)
{
Point currentPoint = e.GetPosition(LockCanvas);
var ellipse = sender as Ellipse;
bool firstEntry = IsItFirstEntry(ellipse);
Point point = GetPointFromCodeEllipse(ellipse);
if (!CheckSequenceExists(ellipse))
{
Shape arrow = null;
if (NewLine.Points.Count - 1 > 0)
{
arrow = DrawLinkArrow(NewLine.Points[NewLine.Points.Count - 1], point,
Convert.ToString(ellipse.Tag));
}
NewLine.Points.Add(point);
if (!chkStealthMode.IsChecked.Value)
{
FillCodeColor(sender as Ellipse, Brushes.Green);
if (arrow != null)
{
LockCanvas.Children.Add(arrow);
}
}
FillCodeSequence(ellipse);
}
if (firstEntry)
{
NewLine.Points.Add(point);
}
}
}
创建/编辑屏幕图案
保存时,图案将保存为 MD5Hash 加密代码,该代码将保存在数据库中。 保存图案后,捕获的屏幕图案将通过电子邮件发送给用户以供参考。
C#
private static BitmapSource CaptureScreen(Visual target, double dpiX, double dpiY)
{
if (target == null)
{
return null;
}
Rect bounds = VisualTreeHelper.GetDescendantBounds(target);
RenderTargetBitmap rtb = new RenderTargetBitmap((int)(bounds.Width * dpiX / 96.0),
(int)(bounds.Height * dpiY / 96.0),
dpiX,
dpiY,
PixelFormats.Pbgra32);
DrawingVisual dv = new DrawingVisual();
using (DrawingContext ctx = dv.RenderOpen())
{
VisualBrush vb = new VisualBrush(target);
ctx.DrawRectangle(vb, null, new Rect(new Point(), bounds.Size));
}
rtb.Render(dv);
return rtb;
}
/// <summary>
/// Encrypt passcode and raise the event to continue using the encrypted passcode
/// </summary>
protected virtual void SavePassCode()
{
if (ScreenLockData != null && ScreenLockData.OnSave != null)
{
var encryptedCode = Encryptor.MD5Hash(CodeSequence.ToStackString());
ScreenLockData.ScreenCode = encryptedCode;
ScreenLockData.TargetScreenCode = encryptedCode;
ScreenLockData.SreenCodeImage = CaptureScreen(LockCanvas, 100, 100);
ScreenLockData.OnSave(ScreenLockData);
timer.Start();
}
}
登录窗口
由于我们已经准备好使用锁屏用户控件,让我们在登录页面中使用它。
将 ScreenLockdata
与类型为 IScreenLockViewModel
的视图模型绑定。 使用处理程序方法注册视图模型委托。
XAML:
<lock:LockView ScreenLockMode="{Binding ScreenLockMode,Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged}"
HorizontalAlignment="Stretch"
Visibility="{Binding ScreenVisibility,Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}"
ScreenLockData="{Binding ScreenLockViewModel, Mode=TwoWay,UpdateSourceTrigger=PropertyChanged}">
</lock:LockView>
/// <summary>
/// Screen lock data
/// </summary>
public IScreenLockViewModel ScreenLockViewModel
{
get { return screenViewModel; }
set
{
screenViewModel = value;
RaisePropertyChanged("ScreenLockViewModel ");
}
}
ScreenLockViewModel = new ScreenLockViewModel();
ScreenLockViewModel.OnScreenCodeValidated = OnScreenCodeValidation;
ScreenLockViewModel.OnSave = OnSave;
ScreenLockViewModel.TargetScreenCode = GetUserCode(user);// get from database;
ScreenLockMode = Extensions.ScreenLockMode.Login;
this.DataContext = this;
/// <summary>
/// Authenticate the user and proceed
/// </summary>
/// <param name="obj"></param>
/// <param name="isValidated"></param>
private void OnScreenCodeValidation(IScreenLockViewModel obj, bool isValidated)
{
if (isValidated)
MessageBox.Show(string.Format("Authenticated Password : {0}", obj.ScreenCode));
}
/// <summary>
/// Create or edit an existing password and save the target screen code
/// </summary>
/// <param name="obj"></param>
private void OnSave(IScreenLockViewModel obj)
{
MessageBox.Show("Saved Successfully!");
ScreenLockViewModel.TargetScreenCode = obj.ScreenCode;
ScreenLockMode = Extensions.ScreenLockMode.Login;
SavedImage = obj.SreenCodeImage;
//Send an email with screen code attachment
}
关注点
- 用于绑定屏幕数据的依赖属性。
- 绘制诸如折线和线段之类的形状。
- 使用几何组来绘制带有箭头的线条。
- 委托。
- 使用 MD5Hash 加密。
- 图案控制可用于锁定屏幕或用于初始身份验证过程。