在本文中,我想展示如何创建信用卡两面的观点。与某些银行应用程序类似,我们可能看到带有隐藏CVC的电子信用卡。我们还将开发相似的卡并按类型实现数字卡验证。让我们开始。
要开始,您需要创建一个.NET MAUI项目:
dotnet new maui -n CreditCardSample
遵循步骤,您需要下载标志性字体,以添加信用卡徽标。您可以通过link找到的字体。为桌面选择字体。您需要获取FontAwesome6-Brands.otf
和FontAwesome6-Regular.otf
并将它们复制到Resources>Fonts
文件夹。下一步,您应该注册字体。转到MainProgram.cs
并添加此代码:
fonts.AddFont("FontAwesome6-Brands.otf", "FA6Brands");
fonts.AddFont("FontAwesome6-Regular.otf", "FA6Regular");
,为方便起见,让我们设置我们将要使用的颜色。在资源文件夹中,您可以找到样式文件夹,也可以找到Colors.xaml
:
<Color x:Key="AmericanExpress">#3177CB</Color>
<Color x:Key="DinersClub">#1B4F8F</Color>
<Color x:Key="Discover">#E9752F</Color>
<Color x:Key="JCB">#9E2911</Color>
<Color x:Key="MasterCard">#394854</Color>
<Color x:Key="Visa">#2867BA</Color>
<Color x:Key="Default">#75849D</Color>
<Color x:Key="HeaderLabelTextColor">#BCBCBC</Color>
<Color x:Key="InfoLabelTextColor">#FFFFFF</Color>
现在,让我们创建几个助手并创建一个助手文件夹。第一个助手将通过付费系统检查卡号。
internal static class CardValidationHelper
{
public static Regex AmericanExpress
= new(@"^3[47][0-9]{13}$");
public static Regex DinersClub
= new(@"^3(?:0[0-5]|[68][0-9])[0-9]{11}$");
public static Regex Discover
= new(@"^6(?:011|5[0-9]{2})[0-9]{12}$");
public static Regex JCB
= new(@"^(?:2131|1800|35\d{3})\d{11}$");
public static Regex MasterCard
= new(@"^(?:5[1-5][0-9]{2}|222[1-9]|22[3-9][0-9]|2[3-6][0-9]{2}|27[01][0-9]|2720)[0-9]{12}$");
public static Regex Visa
= new(@"^4[0-9]{12}(?:[0-9]{3})?$");
}
第二个助手允许通过资源名称设置颜色。
internal static class StringExtensions
{
public static Color ToColorFromResourceKey(this string resourceKey)
{
return Application.Current?.Resources
.MergedDictionaries.First()[resourceKey] as Color;
}
}
最后,让我们创建一个视图文件夹并创建CreditCardView。现在添加此标记,我将解释此代码。
<?xml version="1.0" encoding="utf-8"?>
<Frame xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="CreditCardApp.Views.CreditCardView"
HasShadow="False"
BorderColor="White"
CornerRadius="0">
<Grid>
<Frame x:Name="CardFront"
Grid.Row="0"
Grid.Column="0"
HasShadow="True"
BorderColor="Grey"
HorizontalOptions="Center"
VerticalOptions="Start"
WidthRequest="300"
HeightRequest="200"
CornerRadius="8"
Margin="40,10,40,30"
Padding="20">
<Frame.Resources>
<Style TargetType="Label"
x:Key="HeaderLabelStyle">
<Setter Property="LineBreakMode"
Value="TailTruncation" />
<Setter Property="FontSize"
Value="12" />
<Setter Property="TextColor"
Value="{StaticResource HeaderLabelTextColor}" />
</Style>
<Style TargetType="Label"
x:Key="InfoLabelStyle">
<Setter Property="FontSize"
Value="20" />
<Setter Property="TextColor"
Value="{StaticResource InfoLabelTextColor}" />
</Style>
<Style TargetType="Label"
x:Key="CreditCardImageLabelStyle">
<Setter Property="FontSize"
Value="48" />
<Setter Property="TextColor"
Value="#FFFFFF" />
<Setter Property="HorizontalOptions"
Value="EndAndExpand" />
</Style>
</Frame.Resources>
<Grid ColumnDefinitions="*,*"
ColumnSpacing="30"
RowDefinitions="Auto,Auto,40,Auto,40"
RowSpacing="0">
<Label x:Name="CreditCardImageLabel"
Style="{StaticResource CreditCardImageLabelStyle}"
Grid.Row="0"
Grid.Column="1" />
<Label Text="Card Number"
Style="{StaticResource HeaderLabelStyle}"
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2" />
<Label x:Name="CreditCardNumber"
Style="{StaticResource InfoLabelStyle}"
Grid.Row="2"
Grid.Column="0"
Grid.ColumnSpan="2" />
<Label Text="Expiration"
Style="{StaticResource HeaderLabelStyle}"
Grid.Row="3"
Grid.Column="0" />
<Label x:Name="ExpirationDateLabel"
Style="{StaticResource InfoLabelStyle}"
Grid.Row="4"
Grid.Column="0" />
</Grid>
</Frame>
<Frame x:Name="CardBack"
Grid.Row="0"
HasShadow="True"
BorderColor="Grey"
HorizontalOptions="Center"
VerticalOptions="Start"
WidthRequest="300"
HeightRequest="200"
CornerRadius="8"
Margin="40,10,40,30">
<Frame.Resources>
<Style TargetType="Label"
x:Key="InfoLabelStyle">
<Setter Property="FontSize"
Value="20" />
<Setter Property="TextColor"
Value="{StaticResource InfoLabelTextColor}" />
</Style>
</Frame.Resources>
<StackLayout>
<Grid VerticalOptions="EndAndExpand">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<BoxView BackgroundColor="Grey"
Grid.Column="1"
WidthRequest="100"
HeightRequest="40"
HorizontalOptions="End"
VerticalOptions="End" />
<Label x:Name="CardValidationCodeLabel"
Style="{StaticResource InfoLabelStyle}"
Grid.Column="1"
HorizontalOptions="Center"
VerticalOptions="Center" />
</Grid>
</StackLayout>
</Frame>
<Frame x:Name="Streak" Grid.Row="0" BackgroundColor="#5d3623" WidthRequest="300"
HeightRequest="60" CornerRadius="0" HasShadow="False" VerticalOptions="Start" Margin="40">
</Frame>
</Grid>
</Frame>
因为我们想显示卡的两面,所以我们需要使用不同的帧。每个帧都有自己的状态,不依赖于另一帧。此外,它允许在一个方面实现不同的视图。我将视图分为三个部分:前侧,背面和装饰框架。
public partial class CreditCardView
{
public static readonly BindableProperty CardNumberProperty
= BindableProperty.Create(nameof(CardNumber),
typeof(string), typeof(CreditCardView),
propertyChanged: OnCardNumberChanged);
public string CardNumber
{
get => (string)GetValue(CardNumberProperty);
set => SetValue(CardNumberProperty, value);
}
public static readonly BindableProperty ExpirationDateProperty
= BindableProperty.Create(nameof(ExpirationDate),
typeof(DateTime), typeof(CreditCardView), DateTime.Now,
propertyChanged: OnExpirationDateChanged);
public DateTime ExpirationDate
{
get => (DateTime)GetValue(ExpirationDateProperty);
set => SetValue(ExpirationDateProperty, value);
}
public static readonly BindableProperty CardValidationCodeProperty
= BindableProperty.Create(nameof(CardValidationCode),
typeof(string), typeof(CreditCardView), "-",
propertyChanged: OnCardValidationCodeChanged);
public string CardValidationCode
{
get => (string)GetValue(CardValidationCodeProperty);
set => SetValue(CardValidationCodeProperty, value);
}
private static void OnCardNumberChanged(BindableObject bindable,
object oldValue, object newValue)
{
if (bindable is not CreditCardView creditCardView)
return;
creditCardView.SetCreditCardNumber();
}
private static void OnExpirationDateChanged(BindableObject bindable,
object oldValue, object newValue)
{
if (bindable is not CreditCardView creditCardView)
return;
creditCardView.SetExpirationDate();
}
private static void OnCardValidationCodeChanged(BindableObject bindable,
object oldValue, object newValue)
{
if (bindable is not CreditCardView creditCardView)
return;
creditCardView.SetCardValidationCode();
}
public CreditCardView()
{
InitializeComponent();
CardFront.BackgroundColor = "Default".ToColorFromResourceKey();
CardBack.BackgroundColor = "Default".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf09d";
CreditCardImageLabel.FontFamily = "FA6Regular";
ExpirationDateLabel.Text = "-";
CardValidationCodeLabel.Text = $"CVC: -";
CardBack.IsVisible = false;
CardFront.IsVisible = true;
Streak.IsVisible = false;
var tapGestureRecognizer = new TapGestureRecognizer();
tapGestureRecognizer.Tapped += OnCardTapped;
this.GestureRecognizers.Add(tapGestureRecognizer);
}
private void SetCreditCardNumber()
{
if (string.IsNullOrEmpty(CardNumber))
{
CardFront.BackgroundColor = (Color)Application.Current?.Resources["Default"];
CardBack.BackgroundColor = (Color)Application.Current?.Resources["Default"];
CreditCardImageLabel.Text = "\uf09d";
CreditCardImageLabel.FontFamily = "FA6Regular";
}
if (long.TryParse(CardNumber, out long cardNumberAsLong))
{
CreditCardNumber.Text =
string.Format("{0:0000 0000 0000 0000}", cardNumberAsLong);
}
else
{
CreditCardNumber.Text = "-";
}
if (CardNumber != null)
{
var normalizedCardNumber = CardNumber.Replace("-", string.Empty);
if (CardValidationHelper.AmericanExpress.IsMatch(normalizedCardNumber))
{
CardFront.BackgroundColor = "AmericanExpress".ToColorFromResourceKey();
CardBack.BackgroundColor = "AmericanExpress".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf1f3";
CreditCardImageLabel.FontFamily = "FA6Brands";
}
else if (CardValidationHelper.DinersClub.IsMatch(normalizedCardNumber))
{
CardFront.BackgroundColor = "DinersClub".ToColorFromResourceKey();
CardBack.BackgroundColor = "DinersClub".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf24c";
CreditCardImageLabel.FontFamily = "FA6Brands";
}
else if (CardValidationHelper.Discover.IsMatch(normalizedCardNumber))
{
CardFront.BackgroundColor = "Discover".ToColorFromResourceKey();
CardBack.BackgroundColor = "Discover".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf1f2";
CreditCardImageLabel.FontFamily = "FA6Brands";
}
else if (CardValidationHelper.JCB.IsMatch(normalizedCardNumber))
{
CardFront.BackgroundColor = "JCB".ToColorFromResourceKey();
CardBack.BackgroundColor = "JCB".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf24b";
CreditCardImageLabel.FontFamily = "FA6Brands";
}
else if (CardValidationHelper.MasterCard.IsMatch(normalizedCardNumber))
{
CardFront.BackgroundColor = "MasterCard".ToColorFromResourceKey();
CardBack.BackgroundColor = "MasterCard".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf1f1";
CreditCardImageLabel.FontFamily = "FA6Brands";
}
else if (CardValidationHelper.Visa.IsMatch(normalizedCardNumber))
{
CardFront.BackgroundColor = "Visa".ToColorFromResourceKey();
CardBack.BackgroundColor = "Visa".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf1f0";
CreditCardImageLabel.FontFamily = "FA6Brands";
}
else
{
CardFront.BackgroundColor = "Default".ToColorFromResourceKey();
CardBack.BackgroundColor = "Default".ToColorFromResourceKey();
CreditCardImageLabel.Text = "\uf09d";
CreditCardImageLabel.FontFamily = "FA6Regular";
}
}
}
private void SetExpirationDate()
{
ExpirationDateLabel.Text = ExpirationDate.ToString("MM/yy",
CultureInfo.InvariantCulture);
}
private void SetCardValidationCode()
{
CardValidationCodeLabel.Text = $"CVC: {CardValidationCode}";
}
public static readonly BindableProperty IsFlippedProperty =
BindableProperty.Create(nameof(IsFlipped), typeof(bool), typeof(CreditCardView), false);
public bool IsFlipped
{
get => (bool)GetValue(IsFlippedProperty);
set
{
SetValue(IsFlippedProperty, value);
UpdateCardSideVisibility();
}
}
private void UpdateCardSideVisibility()
{
if (IsFlipped)
{
CardFront.IsVisible = false;
CardBack.IsVisible = true;
Streak.IsVisible = true;
}
else
{
CardFront.IsVisible = true;
CardBack.IsVisible = false;
Streak.IsVisible = false;
}
}
private void OnCardTapped(object sender, EventArgs e)
{
IsFlipped = !IsFlipped;
}
}
让我们解释一下此文件中发生的情况。 CardNumberProperty
绑定了一个卡号。 ExpirationDateProperty
绑定了到期日期。 CardValidationCodeProperty
在后侧绑定了安全码。 IsFlippedProperty
绑定卡状态。 SetCreditCardNumber
为每张卡设置样式。 SetExpirationDate
格式到期日期。 SetCardValidationCode
将标题加入安全码,更方便。 OnCardNumberChanged
,OnExpirationDateChanged
,OnCardValidationCodeChanged
,OnCardTapped
可观察的变化。 UpdateCardSideVisibility
开关帧。
最后一步,让我们使用传递数据添加信用卡视图。
<VerticalStackLayout>
<views:CreditCardView CardNumber="371449635398431"
ExpirationDate="2024-12-01"
CardValidationCode="123"/>
<views:CreditCardView CardNumber="38520000023237"
ExpirationDate="2025-12-01"
CardValidationCode="456"/>
<views:CreditCardView CardNumber="6011000990139424"
ExpirationDate="2026-12-01"
CardValidationCode="789"/>
<views:CreditCardView CardNumber="3566002020360505"
ExpirationDate="2027-12-01"
CardValidationCode="321"/>
<views:CreditCardView CardNumber="5555555555554444"
ExpirationDate="2028-12-01"
CardValidationCode="654"/>
<views:CreditCardView CardNumber="4012888888881881"
ExpirationDate="2028-12-01"
CardValidationCode="987"/>
<views:CreditCardView />
</VerticalStackLayout>
让我们检查一下。
按卡片点击。
您甚至可以用另一张卡来点击。
如果这个主题很有趣,我将在下一篇文章中展示如何在.net Maui中制作动画。现在就这样。在下一篇文章中与您见面!
您可以通过link。
找到的源代码