Implémentation d’une vue

Télécharger l’exemple Télécharger l’exemple

Xamarin.Forms les contrôles d’interface utilisateur personnalisés doivent dériver de la classe View, qui est utilisée pour placer les dispositions et les contrôles à l’écran. Cet article explique comment créer un renderer personnalisé pour un Xamarin.Forms contrôle personnalisé utilisé pour afficher un flux vidéo en préversion à partir de l’appareil photo de l’appareil.

Chaque Xamarin.Forms vue a un renderer d’accompagnement pour chaque plateforme qui crée une instance d’un contrôle natif. Quand un View est rendu par une Xamarin.Forms application dans iOS, la ViewRenderer classe est instanciée, ce qui instancie à son tour un contrôle natif UIView . Sur la plateforme Android, la classe ViewRenderer instancie un contrôle View natif. Sur la plateforme Windows universelle (UWP), la classe ViewRenderer instancie un contrôle FrameworkElement natif. Pour plus d’informations sur le convertisseur et les classes de contrôle natifs qui contrôlent Xamarin.Forms la correspondance, consultez Classes de base du convertisseur et Contrôles natifs.

Notes

Certains contrôles sur Android utilisent des convertisseurs rapides, qui ne consomment pas la ViewRenderer classe . Pour plus d’informations sur les convertisseurs rapides, consultez Xamarin.Forms Fast Renderers.

Le diagramme suivant illustre la relation entre l’élément View et les contrôles natifs correspondants qui l’implémentent :

Relation entre la classe View et ses classes natives d’implémentation

Il est possible d’utiliser le processus de rendu pour implémenter des personnalisations spécifiques à la plateforme en créant un renderer personnalisé pour un élément View sur chaque plateforme. Le processus pour y parvenir est le suivant :

  1. Créez un Xamarin.Forms contrôle personnalisé.
  2. Consommez le contrôle personnalisé à partir de Xamarin.Forms.
  3. Créez le renderer personnalisé pour le contrôle sur chaque plateforme.

Chaque élément va maintenant être abordé tour à tour, afin d’implémenter un renderer CameraPreview qui affiche un flux vidéo d’aperçu à partir de la caméra de l’appareil. Le fait d’appuyer sur le flux vidéo l’arrête et le démarre.

Création du contrôle personnalisé

Un contrôle personnalisé peut être créé en sous-classant la View classe, comme indiqué dans l’exemple de code suivant :

public class CameraPreview : View
{
  public static readonly BindableProperty CameraProperty = BindableProperty.Create (
    propertyName: "Camera",
    returnType: typeof(CameraOptions),
    declaringType: typeof(CameraPreview),
    defaultValue: CameraOptions.Rear);

  public CameraOptions Camera
  {
    get { return (CameraOptions)GetValue (CameraProperty); }
    set { SetValue (CameraProperty, value); }
  }
}

Le contrôle personnalisé CameraPreview est créé dans le projet de bibliothèque .NET Standard et définit l’API pour le contrôle. Le contrôle personnalisé expose une propriété Camera qui est utilisée pour contrôler si le flux vidéo doit être affiché à partir de la caméra avant ou arrière de l’appareil. Si aucune valeur n’est spécifiée pour la propriété Camera lors de la création du contrôle, la caméra arrière est spécifiée par défaut.

Consommation du contrôle personnalisé

Vous pouvez référencer le contrôle personnalisé CameraPreview en XAML dans le projet de bibliothèque .NET Standard en déclarant un espace de noms pour son emplacement et en utilisant le préfixe d’espace de noms sur l’élément de contrôle personnalisé. L’exemple de code suivant montre comment le contrôle personnalisé CameraPreview peut être consommé par une page XAML :

<ContentPage ...
             xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
             ...>
    <StackLayout>
        <Label Text="Camera Preview:" />
        <local:CameraPreview Camera="Rear"
                             HorizontalOptions="FillAndExpand"
                             VerticalOptions="FillAndExpand" />
    </StackLayout>
</ContentPage>

Le préfixe d’espace de noms local peut porter n’importe quel nom. Toutefois, les valeurs clr-namespace et assembly doivent correspondre aux détails du contrôle personnalisé. Une fois l’espace de noms déclaré, le préfixe est utilisé pour référencer le contrôle personnalisé.

L’exemple de code suivant montre comment le contrôle personnalisé CameraPreview peut être consommé par une page C# :

public class MainPageCS : ContentPage
{
  public MainPageCS ()
  {
    ...
    Content = new StackLayout
    {
      Children =
      {
        new Label { Text = "Camera Preview:" },
        new CameraPreview
        {
          Camera = CameraOptions.Rear,
          HorizontalOptions = LayoutOptions.FillAndExpand,
          VerticalOptions = LayoutOptions.FillAndExpand
        }
      }
    };
  }
}

Une instance du contrôle personnalisé CameraPreview est utilisée pour afficher le flux vidéo d’aperçu à partir de la caméra de l’appareil. Indépendamment de la spécification éventuelle d’une valeur pour la propriété Camera, la personnalisation du contrôle est effectuée dans le renderer personnalisé.

Un renderer personnalisé peut maintenant être ajouté à chaque projet d’application pour créer des contrôles d’aperçu de la caméra spécifiques à la plateforme.

Création du renderer personnalisé sur chaque plateforme

Le processus de création de la classe de renderer personnalisé sur iOS et UWP est le suivant :

  1. Créez une sous-classe de la classe ViewRenderer<T1,T2> qui restitue le contrôle personnalisé. Le premier argument de type doit être le contrôle personnalisé auquel le renderer est destiné ; dans le cas présent, CameraPreview. Le deuxième argument de type doit être le contrôle natif qui implémentera le contrôle personnalisé.
  2. Remplacez la méthode OnElementChanged qui restitue le contrôle personnalisé et écrivez une logique pour le personnaliser. Cette méthode est appelée lors de la création du contrôle correspondant Xamarin.Forms .
  3. Ajoutez un ExportRenderer attribut à la classe de renderer personnalisé pour spécifier qu’il sera utilisé pour afficher le Xamarin.Forms contrôle personnalisé. Cet attribut est utilisé pour inscrire le convertisseur personnalisé auprès de Xamarin.Forms.

Le processus de création de la classe de renderer personnalisé sur Android, en tant que convertisseur rapide, est le suivant :

  1. Créez une sous-classe du contrôle Android qui restitue le contrôle personnalisé. En outre, spécifiez que la sous-classe implémente les IVisualElementRenderer interfaces et IViewRenderer .
  2. Implémentez les IVisualElementRenderer interfaces et IViewRenderer dans la classe fast renderer.
  3. Ajoutez un ExportRenderer attribut à la classe de renderer personnalisé pour spécifier qu’il sera utilisé pour afficher le Xamarin.Forms contrôle personnalisé. Cet attribut est utilisé pour inscrire le convertisseur personnalisé auprès de Xamarin.Forms.

Notes

Pour la plupart des Xamarin.Forms éléments, il est facultatif de fournir un renderer personnalisé dans chaque projet de plateforme. Si un renderer personnalisé n’est pas inscrit, le renderer par défaut de la classe de base du contrôle est utilisé. Toutefois, les renderers personnalisés sont nécessaires dans chaque projet de plateforme lors du rendu d’un élément View.

Le diagramme suivant illustre les responsabilités de chaque projet dans l’exemple d’application ainsi que les relations qu’ils entretiennent les uns avec les autres :

Responsabilités du projet de renderer personnalisé CameraPreview

Le CameraPreview contrôle personnalisé est rendu par des classes de renderer spécifiques à la plateforme, qui dérivent de la ViewRenderer classe sur iOS et UWP, et de la FrameLayout classe sur Android. Il en résulte le rendu de chaque contrôle personnalisé CameraPreview avec des contrôles spécifiques à la plateforme, comme le montrent les captures d’écran suivantes :

CameraPreview sur chaque plateforme

La ViewRenderer classe expose la OnElementChanged méthode, qui est appelée lorsque le Xamarin.Forms contrôle personnalisé est créé pour afficher le contrôle natif correspondant. Cette méthode prend un paramètre ElementChangedEventArgs qui contient les propriétés OldElement et NewElement. Ces propriétés représentent l’élément Xamarin.Forms auquel le convertisseur a été attaché et l’élément Xamarin.Forms auquel le convertisseur est attaché, respectivement. Dans l’exemple d’application, la propriété OldElement sera null et la propriété NewElement contiendra une référence à l’instance CameraPreview.

Une version substituée de la méthode OnElementChanged, dans chaque classe de renderer spécifique à la plateforme, est l’emplacement où effectuer l’instanciation et la personnalisation du contrôle natif. La méthode SetNativeControl doit être utilisée pour instancier le contrôle natif, et cette méthode affecte également la référence de contrôle à la propriété Control. En outre, une référence au Xamarin.Forms contrôle en cours de rendu peut être obtenue via la Element propriété .

Dans certains cas, la méthode OnElementChanged peut être appelée plusieurs fois. Ainsi, pour éviter les fuites de mémoire, vous devez être vigilant au moment de l’instanciation d’un nouveau contrôle natif. L’approche à utiliser lors de l’instanciation d’un nouveau contrôle natif dans un renderer personnalisé est indiquée dans l’exemple de code suivant :

protected override void OnElementChanged (ElementChangedEventArgs<NativeListView> e)
{
  base.OnElementChanged (e);

  if (e.OldElement != null)
  {
    // Unsubscribe from event handlers and cleanup any resources
  }

  if (e.NewElement != null)
  {    
    if (Control == null)
    {
      // Instantiate the native control and assign it to the Control property with
      // the SetNativeControl method
    }
    // Configure the control and subscribe to event handlers
  }
}

Un nouveau contrôle natif doit uniquement être instancié une seule fois, quand la propriété Control a la valeur null. En outre, le contrôle doit être créé, configuré et les gestionnaires d’événements abonnés uniquement lorsque le convertisseur personnalisé est attaché à un nouvel Xamarin.Forms élément. De même, vous devez vous désabonner de tous les gestionnaires d’événements auxquels vous vous êtes abonné uniquement quand l’élément auquel le renderer est attaché change. Le choix de cette approche permet de créer un renderer personnalisé performant qui ne subit pas de fuites de mémoire.

Important

La méthode SetNativeControl doit uniquement être appelée si e.NewElement n’est pas null.

Chaque classe de renderer personnalisé est décorée avec un ExportRenderer attribut qui inscrit le renderer avec Xamarin.Forms. L’attribut accepte deux paramètres : le nom de type du Xamarin.Forms contrôle personnalisé en cours de rendu et le nom de type du convertisseur personnalisé. Le préfixe assembly de l’attribut spécifie que l’attribut s’applique à la totalité de l’assembly.

Les sections suivantes décrivent l’implémentation de chaque classe de renderer personnalisé spécifique à la plateforme.

Création du renderer personnalisé sur iOS

L’exemple de code suivant illustre le renderer personnalisé pour la plateforme iOS :

[assembly: ExportRenderer (typeof(CameraPreview), typeof(CameraPreviewRenderer))]
namespace CustomRenderer.iOS
{
    public class CameraPreviewRenderer : ViewRenderer<CameraPreview, UICameraPreview>
    {
        UICameraPreview uiCameraPreview;

        protected override void OnElementChanged (ElementChangedEventArgs<CameraPreview> e)
        {
            base.OnElementChanged (e);

            if (e.OldElement != null) {
                // Unsubscribe
                uiCameraPreview.Tapped -= OnCameraPreviewTapped;
            }
            if (e.NewElement != null) {
                if (Control == null) {
                  uiCameraPreview = new UICameraPreview (e.NewElement.Camera);
                  SetNativeControl (uiCameraPreview);
                }
                // Subscribe
                uiCameraPreview.Tapped += OnCameraPreviewTapped;
            }
        }

        void OnCameraPreviewTapped (object sender, EventArgs e)
        {
            if (uiCameraPreview.IsPreviewing) {
                uiCameraPreview.CaptureSession.StopRunning ();
                uiCameraPreview.IsPreviewing = false;
            } else {
                uiCameraPreview.CaptureSession.StartRunning ();
                uiCameraPreview.IsPreviewing = true;
            }
        }
        ...
    }
}

Sous réserve que la propriété Control ait la valeur null, la méthode SetNativeControl est appelée pour instancier un nouveau contrôle UICameraPreview et lui affecter une référence à la propriété Control. Le contrôle UICameraPreview est un contrôle personnalisé spécifique à la plateforme qui utilise les API AVCapture pour fournir le flux de l’aperçu de la caméra. Il expose un événement Tapped qui est géré par la méthode OnCameraPreviewTapped pour arrêter et démarrer l’aperçu vidéo quand l’utilisateur appuie dessus. L’événement Tapped est abonné lorsque le renderer personnalisé est attaché à un nouvel Xamarin.Forms élément, et est annulé uniquement lorsque l’élément auquel le convertisseur est attaché est modifié.

Création du renderer personnalisé sur Android

L’exemple de code suivant montre le convertisseur rapide pour la plateforme Android :

[assembly: ExportRenderer(typeof(CustomRenderer.CameraPreview), typeof(CameraPreviewRenderer))]
namespace CustomRenderer.Droid
{
    public class CameraPreviewRenderer : FrameLayout, IVisualElementRenderer, IViewRenderer
    {
        // ...
        CameraPreview element;
        VisualElementTracker visualElementTracker;
        VisualElementRenderer visualElementRenderer;
        FragmentManager fragmentManager;
        CameraFragment cameraFragment;

        FragmentManager FragmentManager => fragmentManager ??= Context.GetFragmentManager();

        public event EventHandler<VisualElementChangedEventArgs> ElementChanged;
        public event EventHandler<PropertyChangedEventArgs> ElementPropertyChanged;

        CameraPreview Element
        {
            get => element;
            set
            {
                if (element == value)
                {
                    return;
                }

                var oldElement = element;
                element = value;
                OnElementChanged(new ElementChangedEventArgs<CameraPreview>(oldElement, element));
            }
        }

        public CameraPreviewRenderer(Context context) : base(context)
        {
            visualElementRenderer = new VisualElementRenderer(this);
        }

        void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)
        {
            CameraFragment newFragment = null;

            if (e.OldElement != null)
            {
                e.OldElement.PropertyChanged -= OnElementPropertyChanged;
                cameraFragment.Dispose();
            }
            if (e.NewElement != null)
            {
                this.EnsureId();

                e.NewElement.PropertyChanged += OnElementPropertyChanged;

                ElevationHelper.SetElevation(this, e.NewElement);
                newFragment = new CameraFragment { Element = element };
            }

            FragmentManager.BeginTransaction()
                .Replace(Id, cameraFragment = newFragment, "camera")
                .Commit();
            ElementChanged?.Invoke(this, new VisualElementChangedEventArgs(e.OldElement, e.NewElement));
        }

        async void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            ElementPropertyChanged?.Invoke(this, e);

            switch (e.PropertyName)
            {
                case "Width":
                    await cameraFragment.RetrieveCameraDevice();
                    break;
            }
        }       
        // ...
    }
}

Dans cet exemple, la OnElementChanged méthode crée un CameraFragment objet, à condition que le convertisseur personnalisé soit attaché à un nouvel Xamarin.Forms élément. Le CameraFragment type est une classe personnalisée qui utilise l’API Camera2 pour fournir le flux d’aperçu à partir de l’appareil photo. L’objet CameraFragment est supprimé lorsque l’élément Xamarin.Forms auquel le convertisseur est attaché change.

Création du renderer personnalisé sur UWP

L’exemple de code suivant illustre le renderer personnalisé pour UWP :

[assembly: ExportRenderer(typeof(CameraPreview), typeof(CameraPreviewRenderer))]
namespace CustomRenderer.UWP
{
    public class CameraPreviewRenderer : ViewRenderer<CameraPreview, Windows.UI.Xaml.Controls.CaptureElement>
    {
        ...
        CaptureElement _captureElement;
        bool _isPreviewing;

        protected override void OnElementChanged(ElementChangedEventArgs<CameraPreview> e)
        {
            base.OnElementChanged(e);

            if (e.OldElement != null)
            {
                // Unsubscribe
                Tapped -= OnCameraPreviewTapped;
                ...
            }
            if (e.NewElement != null)
            {
                if (Control == null)
                {
                  ...
                  _captureElement = new CaptureElement();
                  _captureElement.Stretch = Stretch.UniformToFill;

                  SetupCamera();
                  SetNativeControl(_captureElement);
                }
                // Subscribe
                Tapped += OnCameraPreviewTapped;
            }
        }

        async void OnCameraPreviewTapped(object sender, TappedRoutedEventArgs e)
        {
            if (_isPreviewing)
            {
                await StopPreviewAsync();
            }
            else
            {
                await StartPreviewAsync();
            }
        }
        ...
    }
}

Sous réserve que la propriété Control ait la valeur null, un nouveau CaptureElement est instancié et la méthode SetupCamera est appelée, entraînant l’utilisation de l’API MediaCapture afin de fournir le flux d’aperçu à partir de la caméra. La méthode SetNativeControl est ensuite appelée pour affecter une référence à l’instance CaptureElement à la propriété Control. Le contrôle CaptureElement expose un événement Tapped qui est géré par la méthode OnCameraPreviewTapped pour arrêter et démarrer l’aperçu vidéo quand l’utilisateur appuie dessus. L’événement Tapped est abonné lorsque le renderer personnalisé est attaché à un nouvel Xamarin.Forms élément, et est annulé uniquement lorsque l’élément auquel le convertisseur est attaché est modifié.

Notes

Il est important d’arrêter et de supprimer les objets qui fournissent un accès à la caméra dans une application UWP. Si vous ne le faites pas, il peut se produire une interférence avec d’autres applications qui tentent d’accéder à la caméra de l’appareil. Pour plus d’informations, consultez Afficher l’aperçu de la caméra.

Résumé

Cet article a montré comment créer un renderer personnalisé pour un Xamarin.Forms contrôle personnalisé qui est utilisé pour afficher un flux vidéo en préversion à partir de la caméra de l’appareil. Xamarin.Forms les contrôles d’interface utilisateur personnalisés doivent dériver de la View classe , qui est utilisée pour placer les dispositions et les contrôles à l’écran.