Xamarin Forms : Créer une vue avec un arrière-plan dégradé à l'aide de custom renderers
Difficile d'échapper aux dégradés dans les designs d'applications mobiles de nos jours, mais ce n'est pas la chose la plus évidente à faire en Xamarin.Forms ! En effet, aucun moyen de le faire sans passer par des renderers.
Dans cet article, nous allons donc voir comment créer une vue qui aura un arrière-plan dégradé comme ceci :
Projet commun
Tout d'abord, dans le projet commun, commençons par créer notre vue qui aura un dégradé comme arrière-plan. Comme je veux que ma vue puisse contenir n'importe quel type de contenu, je la fais étendre ContentView
, mais libre à vous de choisir quel contrôle étendre.
Cette vue contient 3 propriétés "bindable" :
- StartColor : la couleur de départ du dégradé
- EndColor : la couleur de fin du dégradé
- Orientation : le sens du dégradé (horizontal, ou vertical)
Ci-dessous, le code complet du control GradientView
:
public class GradientView : ContentView { public static readonly BindableProperty StartColorProperty = BindableProperty.Create( nameof(StartColor), typeof(Color), typeof(GradientView), defaultBindingMode: BindingMode.OneWay, defaultValue: default(Color)); public Color StartColor { get { return (Color)GetValue(StartColorProperty); } set { SetValue(StartColorProperty, value); } } public static readonly BindableProperty EndColorProperty = BindableProperty.Create( nameof(EndColor), typeof(Color), typeof(GradientView), defaultBindingMode: BindingMode.OneWay, defaultValue: default(Color)); public Color EndColor { get { return (Color)GetValue(EndColorProperty); } set { SetValue(EndColorProperty, value); } } public static readonly BindableProperty OrientationProperty = BindableProperty.Create( nameof(Orientation), typeof(StackOrientation), typeof(GradientView), defaultBindingMode: BindingMode.OneWay, defaultValue: StackOrientation.Horizontal); public StackOrientation Orientation { get { return (StackOrientation)GetValue(OrientationProperty); } set { SetValue(OrientationProperty, value); } } }
Malheureusement, ceci n'est pas suffisant pour dessiner notre dégradé. Passons donc au code spécifique aux plateformes Android et iOS.
Projet Android
Dans le projet Android, créons une nouvelle classe qui contiendra notre renderer. Si vous n'êtes pas familier avec la notion de renderer, ils permettent de personnaliser l'apparence et le comportements des différents contrôles. Pour plus de détails, vous réferer à la documentation officielle. Dans notre cas, le renderer permettra de personnaliser l'apparence de la ContentView
afin d'y appliquer notre dégradé.
Voici à quoi doit ressembler notre renderer au départ et notez la présence des méthodes OnElementChanged
et OnElementPropertyChanged
que nous implémenterons tout à l'heure. La première nous servira à dessiner l'arrière plan lorsque le contrôle change, notamment lorsque qu'il est construit, et la deuxième lorqu'une propriété change, par exemple lorsqu'une couleur sera changée à l'exécution.
using Android.Content; using GradientViewSample.Controls; using GradientViewSample.Droid.Renderers; using System; using System.ComponentModel; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: Xamarin.Forms.ExportRenderer(typeof(GradientView), typeof(DroidGradientViewRenderer))] namespace GradientViewSample.Droid.Renderers { public class DroidGradientViewRenderer : VisualElementRenderer<ContentView> { public DroidGradientViewRenderer(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<ContentView> e) { base.OnElementChanged(e); } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); } } }
Implémentons donc notre renderer. Tout d'abord, commençons par ajouter les propriétés de notre contrôle ainsi que la méthode qui dessinera l'arrière plan :
private Color StartColor { get; set; } private Color EndColor { get; set; } private StackOrientation Orientation { get; set; } private void SetGradientBackground() { int[] colors = { StartColor.ToAndroid(), EndColor.ToAndroid() }; GradientDrawable gradient = new GradientDrawable( Orientation == StackOrientation.Horizontal ? GradientDrawable.Orientation.LeftRight : GradientDrawable.Orientation.TopBottom, colors); Android.Support.V4.View.ViewCompat.SetBackground(this, gradient); }
Puis implémentons les méthodes OnElementChanged
et OnElementPropertyChanged
:
protected override void OnElementChanged(ElementChangedEventArgs<ContentView> e) { base.OnElementChanged(e); if (e.OldElement != null || Element == null) { return; } try { var stack = (GradientView)e.NewElement; StartColor = stack.StartColor; EndColor = stack.EndColor; Orientation = stack.Orientation; SetGradientBackground(); } catch (Exception ex) { System.Diagnostics.Debug.WriteLine(@"ERROR:", ex.Message); } } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); if (e.PropertyName == nameof(GradientView.StartColor) || e.PropertyName == nameof(GradientView.EndColor)) { var stack = (GradientView)sender; if (stack == null) { return; } StartColor = stack.StartColor; EndColor = stack.EndColor; Orientation = stack.Orientation; SetGradientBackground(); } }
Voilà, notre renderer Android est terminé. Si vous ne souhaitez ou ne pouvez pas implémenter la partie concernant iOS, vous pouvez sauter la partie suivante pour passer directement à l'utilisation de notre nouveau contrôle.
Projet iOS
Maintenant, dans le projet iOS, de la même manière que pour le projet Android, créons une classe qui contiendra notre renderer. A la différence d'Android, ici, nous n'utiliserons que la méthode OnElementPropertyChanged
couplée à la méthode Draw
pour dessiner notre dégradé.
using System.ComponentModel; using GradientViewSample.iOS.Renderers; using GradientViewSample.Controls; using Xamarin.Forms; using Xamarin.Forms.Platform.iOS; [assembly: ExportRenderer(typeof(GradientView), typeof(IOSGradientViewRenderer))] namespace GradientViewSample.iOS.Renderers { public class IOSGradientViewRenderer : VisualElementRenderer<ContentView> { public override void Draw(CGRect rect) { base.Draw(rect); } protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e) { base.OnElementPropertyChanged(sender, e); } } }
Implémentons maintenant notre renderer. La méthode OnElementPropertyChanged
, qui est appelée lorsqu'une propriété du contrôle change, ici appelera la méthode SetNeedsDisplay
pour demander à redessiner le contrôle. C'est la méthode Draw
qui s'occupe de dessiner l'interface. Elle récupère donc les couleurs définies dans le contrôle et crée un CAGradientLayer
en fonction de l'orientation choisie.
C'en est fini pour le renderer iOS.
Utilisation
Passons maintenant à l'utilisation de nouveau contrôle. Pour cela, rien de plus simple, il suffit d'ajouter le contrôle sur une page.
<?xml version="1.0" encoding="utf-8" ?> <ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="GradientViewSample.MainPage" xmlns:ctrls="clr-namespace:GradientViewSample.Controls"> <ctrls:GradientView StartColor="#4297ef" EndColor="#002f90" Orientation="Vertical"> <StackLayout> <Label Text="Gradient view sample" TextColor="White" HorizontalOptions="Center" VerticalOptions="CenterAndExpand"/> </StackLayout> </ctrls:GradientView> </ContentPage>
Si tout s'est bien passé, en démarrant l'application vous devriez avoir des rendus similaires à ceux-ci :
TADA ! En voilà de beaux dégradés :)
Vous pourrez retrouver tout le code source ici.
D'autres exemples d'utilisation des custom renderers avec Thomas ici et ici
Commentaires