• laurent@lioncoding.com

Xamarin UI Challenge - Credit Card Checkout


👉 https://github.com/egbakou/CreditCardCheckout le lien vers le projet complet sur Github.

La conception de fantastiques interfaces utilisateur en développement d’applications mobile est un facteur crucial dans l’amélioration de l’expérience utilisateur. Le but de cet article est de vous montrer comment implémenter les interfaces se trouvant sur la capture ci-dessus dans Xamarin.Forms.

Tout d’abord, je dois mentionner que le design de la capture que nous allons reproduire a été créé par Antra Sharma sur Dribble. Les éléments qui ont attiré mon attention sur sa configuration sont les suivants:

  • La police utilisée;
  • La couleur d’arrière plan;
  • Le design des cartes de crédits;
  • Le défilement horizontal des cartes de crédit;
  • Les raccordements entre les petits cercles coloriés et les lignes verticales;
  • Les contours des boutons.

Sérieusement, il suffit de vérifier vous-même pour constater l’élégance du design.

Les librairies utilisées pour ce projet de UI Challenge

Cardsview nous permettra de mettre en place le système de défilement entre les cartes de crédit.

Forms.controls.FlexButton, pour la mise en place de supers boutons avec des contours.

Xam.Plugins.ImageCircle permet d’appliquer des transformations de bordure à des images.

Et l’incontournable Xamarin.Forms.PancakeView pour la conception de contrôles avec des bordures personnalisées.

L’usage de CardsView et ImageCircle impose leur initialisation sur chaque plateforme du projet Xamarin.Forms

Pour le projet Android, dans le MainActivity.cs et pour projet IOS, le fichier AppDelegate.cs

CardsViewRenderer.Preserve();
ImageCircleRenderer.Init();

Implémentations des éléments clés

La police utilisée

Ropa Sans est la police utilisée. Je l’ai téléchargé sur Google Fonts. Dans le fichier App.xaml, il nous faut ajouter les ressources indiquant la présence de cette police.

<Application.Resources>

        <!--  CUSTOM FONTS  -->
        <OnPlatform x:Key="RopaSans" x:TypeArguments="x:String">
            <On Platform="Android">RopaSans-Regular.ttf#Ropa Sans</On>
            <On Platform="iOS">RopaSans-Regular</On>
        </OnPlatform>

        <!--  STYLES  -->
        <Style x:Key="LabeStyle" TargetType="Label">
            <Setter Property="FontFamily" Value="{StaticResource RopaSans}" />
            <Setter Property="TextColor" Value="White" />
        </Style>

</Application.Resources>

L’usage de polices personnalisées sur IOS nécessite l’ajout de quelques lignes de configurations dans le fichier Info.plist:

<key>UIAppFonts</key>
    <array>
    <string>RopaSans-Regular.ttf</string>
</array>

La couleur d’arrière plan

<ContentPage
    BackgroundColor="#2C3359"

Rendons invisible la barre de navigation

Sur la première image utilisée en début d’article, les interfaces présentes n’ont pas de barre de navigation. Celle-ci a été cachée dans le code behind de chaque interface à l’aide de la ligne de code suivante.

NavigationPage.SetHasNavigationBar(this, false);

Le code source pour concevoir une carte de crédit

Ici, nous ferons usage de PancakeView pour le design des bordures de la carte de crédit, des BoxView pour les deux petits cercles coloriés, visibles sur la carte de crédit. Nous utiliserons FlexButton pour concevoir le Bouton Select en vert, raccordé à la carte de crédit.

<StackLayout>
        <pancake:PancakeView
            BackgroundGradientAngle="180"
            BackgroundGradientEndColor="{Binding BackgroundGradientEndColor}"
            BackgroundGradientStartColor="{Binding BackgroundGradientStartColor}"
            CornerRadius="20"
            Elevation="4"
            HasShadow="True"
            HeightRequest="200"
            HorizontalOptions="FillAndExpand">
            <Grid
                Margin="20,20,20,20"
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand">
                <Grid.RowDefinitions>
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                    <RowDefinition Height="*" />
                </Grid.RowDefinitions>
                <StackLayout
                    Grid.Row="0"
                    HorizontalOptions="FillAndExpand"
                    Orientation="Horizontal">
                    <Label
                        FontSize="20"
                        HorizontalOptions="StartAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="{Binding Bank}" />

                    <!-- 2 CIRCLES -->
                    <BoxView
                        CornerRadius="240"
                        HeightRequest="30"
                        HorizontalOptions="End"
                        VerticalOptions="Start"
                        WidthRequest="30"
                        Color="{Binding FirstSignColor}" />
                    <BoxView
                        Margin="-20,0,0,0"
                        CornerRadius="240"
                        HeightRequest="30"
                        HorizontalOptions="End"
                        VerticalOptions="Start"
                        WidthRequest="30"
                        Color="{Binding LastSignColor}" />
                </StackLayout>
                <StackLayout
                    Grid.Row="1"
                    HorizontalOptions="Start"
                    VerticalOptions="EndAndExpand">
                    <Label
                        FontSize="18"
                        Style="{StaticResource LabeStyle}"
                        Text="{Binding CardNumber}" />
                </StackLayout>
                <StackLayout
                    Grid.Row="2"
                    HorizontalOptions="FillAndExpand"
                    Orientation="Horizontal"
                    VerticalOptions="End">
                    <Label
                        FontSize="18"
                        HorizontalOptions="StartAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="{Binding Owner}" />
                    <Label
                        FontSize="16"
                        HorizontalOptions="EndAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="{Binding ExpirationDate}" />
                </StackLayout>
            </Grid>
        </pancake:PancakeView>
        <!-- Select Button -->
        <flex:FlexButton
            Margin="0,-15,20,0"
            BackgroundColor="#22863A"
            CornerRadius="20"
            FontFamily="{StaticResource RopaSans}"
            FontSize="16"
            ForegroundColor="#ffffff"
            HeightRequest="30"
            HorizontalOptions="EndAndExpand"
            Text="Select"
            VerticalOptions="Start" />
    </StackLayout>

Il est préférable de faire du code source des cartes de crédit, un template, pour les réutiliser sur les deux interfaces à concevoir. Vous constaterez que j’ai crée un fichier CardTemplate.xaml contenant le code source dans le projet disponible sur Github.

Le code de l’avatar

Le contour circulaire est possible grâce à la librairie Xam.Plugins.ImageCircle.

<controls:CircleImage
    Aspect="AspectFill"
    BorderColor="#DCA688"
    HeightRequest="40"
    HorizontalOptions="End"
    Source="me.jpg"
    VerticalOptions="Center"
    WidthRequest="40" />

Le système de défilement horizontal entre les cartes de crédit

La librairie Cardsview nous aidera à mettre en place ce défilement. Il faut souligner q’elle propose différents sortes de défilement; défilement en Carousel, en Cards, en Cube ainsi que d’autres. Nous utiliserons le défilement en Cards. Certains peuvent juger idéale celui en Carousel. D’autres utiliserons carrément un CollectionView ou CarouselView de Xamarin.Forms. D’ailleurs, sur les issues et pull requests du projet, disponible sur Github, des propositions dans ce sens ont été déjà faites par certains développeurs.

<cards:CardsView ItemsSource="{Binding Cards}" VerticalOptions="CenterAndExpand">
    <cards:CardsView.ItemTemplate>
        <DataTemplate>
            <template:CardTemplate />
        </DataTemplate>
    </cards:CardsView.ItemTemplate>                       
</cards:CardsView>

Pour ceux qui souhaitent utiliser le défilement en Carousel, voici le code correspondant:

<cards:CarouselView ItemsSource="{Binding Cards}" VerticalOptions="CenterAndExpand">
    <cards:CarouselView.ItemTemplate>
        <DataTemplate>
            <template:CardTemplate />
        </DataTemplate>
    </cards:CarouselView.ItemTemplate>                       
</cards:CarouselView>

Les raccordements entre les petits cercles coloriés et les lignes verticales

Une BoxView circulaire et une, verticale nous aiderons à mettre en place les raccordements.

<StackLayout Orientation="Horizontal">
    <!--  GREEN CIRCLE WITH LABEL  -->
    <BoxView
        CornerRadius="240"
        HeightRequest="25"
        HorizontalOptions="Start"
        VerticalOptions="Start"
        WidthRequest="25"
        Color="Green" />
    <!-- OTHER INLINE ELEMENTS -->                                
</StackLayout>
<!--  VERTICAL WHITE LINE  -->
<BoxView
    Margin="10,-10"
    HeightRequest="30"
    HorizontalOptions="Start"
    WidthRequest="3"
    Color="White" />

Le Bouton PAY NOW

Ce bouton est l’œuvre du NuGet Package FlexButton. Le code source correspondant est le suivant:

<!--  PAY BUTTON  -->
<flex:FlexButton
    Grid.Row="4"
    Padding="0"
    BackgroundColor="White"
    BorderColor="#49516F"
    BorderThickness="0,0,0,0"
    Clicked="FlexButton_Clicked"
    CornerRadius="20"
    FontFamily="{StaticResource RopaSans}"
    FontSize="25"
    ForegroundColor="#2C3359"
    HasShadow="false"
    HeightRequest="55"
    HighlightBackgroundColor="#8EA4D2"
    HighlightBorderColor="#6279B8"
    HighlightForegroundColor="#49516F"
    HorizontalOptions="FillAndExpand"
    Icon="right_arrow.png"
    IconOrientation="Right"
    Text="PAY NOW" />

Code source complet de la première interface: CreditCardView.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="ShopTimeline.Views.CreditCardView"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:cards="clr-namespace:PanCardView;assembly=PanCardView"
    xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:flex="clr-namespace:Flex.Controls;assembly=Flex"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:template="clr-namespace:ShopTimeline.Views.Templates"
    BackgroundColor="#2C3359"
    mc:Ignorable="d">
    <ContentPage.Content>

        <!--  ALL CONTENT IN SCROLLVIEW  -->
        <ScrollView
            HorizontalScrollBarVisibility="Never"
            VerticalOptions="FillAndExpand"
            VerticalScrollBarVisibility="Never">

            <!--  GRID OF 5 ROWS  -->
            <Grid
                Padding="15,10,15,10"
                ColumnSpacing="0"
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <!--  HEADER  -->
                <Grid Grid.Row="0" HeightRequest="50">
                    <Label
                        FontSize="24"
                        HorizontalOptions="CenterAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="CREDIT CARD"
                        VerticalOptions="Center" />
                    <controls:CircleImage
                        Aspect="AspectFill"
                        BorderColor="#DCA688"
                        HeightRequest="40"
                        HorizontalOptions="End"
                        Source="me.jpg"
                        VerticalOptions="Center"
                        WidthRequest="40" />
                </Grid>

                <!--  ADD NEW ITEM SECTION  -->
                <Grid Grid.Row="1" HeightRequest="30">
                    <flex:FlexButton
                        Padding="0"
                        BackgroundColor="#2C3359"
                        BorderColor="White"
                        CornerRadius="38"
                        FontFamily="{StaticResource RopaSans}"
                        FontSize="30"
                        ForegroundColor="#ffffff"
                        HeightRequest="30"
                        HighlightBackgroundColor="#8EA4D2"
                        HighlightBorderColor="White"
                        HighlightForegroundColor="#49516F"
                        HorizontalOptions="End"
                        Text="+"
                        WidthRequest="30" />
                </Grid>

                <!--  AMOUNT TO PAY  -->
                <StackLayout
                    Grid.Row="2"
                    HorizontalOptions="FillAndExpand"
                    VerticalOptions="Center">
                    <Label
                        FontSize="20"
                        HorizontalOptions="CenterAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="PAY" />
                    <Label
                        FontSize="40"
                        HorizontalOptions="CenterAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="Rs.8000" />
                </StackLayout>


                <!--  CARDS SECTION  -->
                <StackLayout
                    Grid.Row="3"
                    Margin="0,20,0,20"
                    HeightRequest="240"
                    HorizontalOptions="FillAndExpand">
                    <cards:CardsView ItemsSource="{Binding Cards}" VerticalOptions="CenterAndExpand">
                        <cards:CardsView.ItemTemplate>
                            <DataTemplate>
                                <template:CardTemplate />
                            </DataTemplate>
                        </cards:CardsView.ItemTemplate>
                    </cards:CardsView>
                    <Label
                        Margin="30,0,0,0"
                        FontSize="16"
                        HorizontalOptions="StartAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="Double click the card to edit details" />
                </StackLayout>

                <!--  PAY BUTTON  -->
                <flex:FlexButton
                    Grid.Row="4"
                    Padding="0"
                    BackgroundColor="White"
                    BorderColor="#49516F"
                    BorderThickness="0,0,0,0"
                    Clicked="FlexButton_Clicked"
                    CornerRadius="20"
                    FontFamily="{StaticResource RopaSans}"
                    FontSize="25"
                    ForegroundColor="#2C3359"
                    HasShadow="false"
                    HeightRequest="55"
                    HighlightBackgroundColor="#8EA4D2"
                    HighlightBorderColor="#6279B8"
                    HighlightForegroundColor="#49516F"
                    HorizontalOptions="FillAndExpand"
                    Icon="right_arrow.png"
                    IconOrientation="Right"
                    Text="PAY NOW" />
            </Grid>
        </ScrollView>
    </ContentPage.Content>
</ContentPage>

Code source complet de la seconde interface: ShopView.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
    x:Class="ShopTimeline.Views.ShopView"
    xmlns="http://xamarin.com/schemas/2014/forms"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:cards="clr-namespace:PanCardView;assembly=PanCardView"
    xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin"
    xmlns:d="http://xamarin.com/schemas/2014/forms/design"
    xmlns:flex="clr-namespace:Flex.Controls;assembly=Flex"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:pancake="clr-namespace:Xamarin.Forms.PancakeView;assembly=Xamarin.Forms.PancakeView"
    xmlns:template="clr-namespace:ShopTimeline.Views.Templates"
    BackgroundColor="#2C3359"
    mc:Ignorable="d">
    <ContentPage.Content>

        <!--  ALL CONTENT IN SCROLLVIEW  -->
        <ScrollView
            HorizontalScrollBarVisibility="Never"
            VerticalOptions="FillAndExpand"
            VerticalScrollBarVisibility="Never">

            <!--  GRID OF 2 ROWS  -->
            <Grid
                Padding="15,5,15,10"
                ColumnSpacing="0"
                HorizontalOptions="FillAndExpand"
                VerticalOptions="FillAndExpand">
                <Grid.RowDefinitions>
                    <RowDefinition Height="Auto" />
                    <RowDefinition Height="Auto" />
                </Grid.RowDefinitions>

                <!--  HEADER  -->
                <Grid Grid.Row="0" HeightRequest="50">
                    <Label
                        FontSize="24"
                        HorizontalOptions="CenterAndExpand"
                        Style="{StaticResource LabeStyle}"
                        Text="SHOP"
                        VerticalOptions="Center" />
                    <controls:CircleImage
                        Aspect="AspectFill"
                        BorderColor="#DCA688"
                        HeightRequest="40"
                        HorizontalOptions="End"
                        Source="me.jpg"
                        VerticalOptions="Center"
                        WidthRequest="40" />
                </Grid>

                <!--  TIMELINE  -->
                <StackLayout Grid.Row="1">
                    <StackLayout Orientation="Horizontal">
                        <!--  GREEN CIRCLE WITH LABEL  -->
                        <BoxView
                            CornerRadius="240"
                            HeightRequest="25"
                            HorizontalOptions="Start"
                            VerticalOptions="Start"
                            WidthRequest="25"
                            Color="Green" />
                        <Label
                            Margin="10,0"
                            FontSize="25"
                            Style="{StaticResource LabeStyle}"
                            Text="ORDER SUMMARY" />
                    </StackLayout>
                    <!--  VERTICAL WHITE LINE  -->
                    <BoxView
                        Margin="10,-10"
                        HeightRequest="30"
                        HorizontalOptions="Start"
                        WidthRequest="3"
                        Color="White" />
                    <!--  TOTAL AMOUNT IN PANCAKEVIEW  -->
                    <pancake:PancakeView
                        BackgroundColor="#52597F"
                        CornerRadius="15"
                        Elevation="4"
                        HasShadow="False"
                        HeightRequest="110"
                        HorizontalOptions="FillAndExpand">
                        <StackLayout Margin="20,20">
                            <Label
                                FontSize="20"
                                Style="{StaticResource LabeStyle}"
                                Text="TIARA RING" />
                            <BoxView
                                Margin="0,0"
                                HeightRequest="1"
                                HorizontalOptions="FillAndExpand"
                                VerticalOptions="Center"
                                Color="White" />
                            <StackLayout Margin="0,5" Orientation="Horizontal">
                                <Label
                                    FontSize="20"
                                    HorizontalOptions="Start"
                                    Style="{StaticResource LabeStyle}"
                                    Text="Total" />
                                <Label
                                    FontSize="30"
                                    HorizontalOptions="EndAndExpand"
                                    Style="{StaticResource LabeStyle}"
                                    Text="Rs.8000" />
                            </StackLayout>
                        </StackLayout>
                    </pancake:PancakeView>
                    <!--  VERTICAL WHITE LINE  -->
                    <BoxView
                        Margin="10,-6"
                        HeightRequest="30"
                        HorizontalOptions="Start"
                        WidthRequest="3"
                        Color="White" />

                    <!--  RED CIRCLE WITH LABEL AND FLEXBUTTON  -->
                    <StackLayout Orientation="Horizontal" VerticalOptions="Center">
                        <BoxView
                            CornerRadius="240"
                            HeightRequest="25"
                            HorizontalOptions="Start"
                            VerticalOptions="Start"
                            WidthRequest="25"
                            Color="#F55C5C" />
                        <Label
                            Margin="10,0,0,0"
                            FontSize="25"
                            Style="{StaticResource LabeStyle}"
                            Text="PAYEMENT" />
                        <flex:FlexButton
                            Padding="0"
                            BackgroundColor="#2C3359"
                            BorderColor="White"
                            CornerRadius="38"
                            FontFamily="{StaticResource RopaSans}"
                            FontSize="30"
                            ForegroundColor="#ffffff"
                            HeightRequest="30"
                            HighlightBackgroundColor="#8EA4D2"
                            HighlightBorderColor="White"
                            HighlightForegroundColor="#49516F"
                            HorizontalOptions="EndAndExpand"
                            Text="+"
                            WidthRequest="30" />
                    </StackLayout>
                    <!--  VERTICAL WHITE LINE  -->
                    <BoxView
                        Margin="10,-10"
                        HeightRequest="30"
                        HorizontalOptions="Start"
                        WidthRequest="3"
                        Color="White" />

                    <!--  CARDS VIEWS  -->
                    <StackLayout
                        Margin="0,0,0,0"
                        HeightRequest="240"
                        HorizontalOptions="FillAndExpand">
                        <cards:CardsView ItemsSource="{Binding Cards}" VerticalOptions="CenterAndExpand">
                            <cards:CardsView.ItemTemplate>
                                <DataTemplate>
                                    <template:CardTemplate />
                                </DataTemplate>
                            </cards:CardsView.ItemTemplate>
                        </cards:CardsView>
                        <Label
                            Margin="30,0,0,0"
                            FontSize="16"
                            HorizontalOptions="StartAndExpand"
                            Style="{StaticResource LabeStyle}"
                            Text="Double click the card to edit details" />
                    </StackLayout>
                    <!--  VERTICAL WHITE LINE  -->
                    <BoxView
                        Margin="10,-52,0,-10"
                        HeightRequest="55"
                        HorizontalOptions="Start"
                        WidthRequest="3"
                        Color="White" />


                    <!--  PAYEMENT FLEXBUTTON  -->
                    <flex:FlexButton
                        Margin="0,0"
                        BackgroundColor="White"
                        BorderColor="#49516F"
                        BorderThickness="0,0,0,0"
                        ClickedCommand="{Binding ButtonClickedCommand}"
                        CornerRadius="20"
                        FontFamily="{StaticResource RopaSans}"
                        FontSize="25"
                        ForegroundColor="#2C3359"
                        HasShadow="false"
                        HeightRequest="55"
                        HighlightBackgroundColor="#8EA4D2"
                        HighlightBorderColor="#6279B8"
                        HighlightForegroundColor="#49516F"
                        HorizontalOptions="FillAndExpand"
                        Icon="right_arrow.png"
                        IconOrientation="Right"
                        Text="PAY NOW" />
                </StackLayout>
            </Grid>
        </ScrollView>
    </ContentPage.Content>
</ContentPage>

Nous voici arrivez à la fin de cet article qui aurait pu être assez long. J’ai donc mis en avant en avant les élément clés de l’implémentation du design pour permettre une appréhension plus rapide.

Il serait intéressant de cloner le code source du projet sur Github pour parfaire la conception du design. Vous pourriez par là faire des améliorations et me les soumettre sur Github sous forme de Pull Request comme l’a fait @devoirtechsandip. Je profite de l’occasion pour le remercier pour sa contribution, même si je n’ai pas encore fait l’intégration de son travail dans le projet initial.

Ressources

👉 https://github.com/egbakou/CreditCardCheckout le lien vers le projet complet sur Github.

N’hésitez pas à me contacter via le formulaire de contact ou par ou par mail.


Commentaires