Partager via


Implémentation d’une vue

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

Chaque Xamarin.Forms vue a un renderer associé pour chaque plateforme qui crée une instance d’un contrôle natif. Lorsqu’une ViewXamarin.Forms application est rendue dans iOS, la ViewRenderer classe est instanciée, 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 les classes de renderer et de contrôle natives qui contrôlent Xamarin.Forms la correspondance, consultez Les classes de base du renderer et les contrôles natifs.

Remarque

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

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

Relation entre la classe View et son implémentation de classes natives

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 utilisant une sous-classe de la classe View, 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 la logique pour le personnaliser. Cette méthode est appelée lorsque le contrôle correspondant Xamarin.Forms est créé.
  3. Ajoutez un ExportRenderer attribut à la classe de renderer personnalisée pour spécifier qu’il sera utilisé pour afficher le Xamarin.Forms contrôle personnalisé. Cet attribut est utilisé pour inscrire le renderer personnalisé avec Xamarin.Forms.

Le processus de création de la classe de renderer personnalisé sur Android, comme 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 interfaces et IViewRenderer les IVisualElementRenderer interfaces.
  2. Implémentez les interfaces et IViewRenderer les IVisualElementRenderer interfaces dans la classe de renderer rapide.
  3. Ajoutez un ExportRenderer attribut à la classe de renderer personnalisée pour spécifier qu’il sera utilisé pour afficher le Xamarin.Forms contrôle personnalisé. Cet attribut est utilisé pour inscrire le renderer personnalisé avec Xamarin.Forms.

Remarque

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, 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 renderer a été attaché, et l’élément Xamarin.Forms auquel le renderer 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. Par conséquent, pour éviter les fuites de mémoire, vous devez être vigilant lors 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 ne doit être créé, configuré et que les gestionnaires d’événements sont abonnés lorsque le renderer 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ée est décorée avec un ExportRenderer attribut qui inscrit le renderer avec Xamarin.Forms. L’attribut prend deux paramètres : le nom de type du Xamarin.Forms contrôle personnalisé en cours de rendu et le nom de type du renderer 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 se désabonne uniquement lorsque l’élément du renderer est attaché aux modifications.

Création du renderer personnalisé sur Android

L’exemple de code suivant montre le renderer 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 renderer 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 la caméra. L’objet CameraFragment est supprimé lorsque l’élément auquel le Xamarin.Forms renderer est attaché aux modifications.

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 se désabonne uniquement lorsque l’élément du renderer est attaché aux modifications.

Remarque

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é utilisé pour afficher un flux vidéo d’aperçu à 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 des dispositions et des contrôles sur l’écran.