Implementazione di una vista

Xamarin.Forms I controlli dell'interfaccia utente personalizzati devono derivare dalla classe View, usata per posizionare layout e controlli sullo schermo. Questo articolo illustra come creare un renderer personalizzato per un Xamarin.Forms controllo personalizzato usato per visualizzare un flusso video di anteprima dalla fotocamera del dispositivo.

Ogni Xamarin.Forms visualizzazione ha un renderer a discesa per ogni piattaforma che crea un'istanza di un controllo nativo. Quando un View oggetto viene sottoposto a rendering da un'applicazione Xamarin.Forms in iOS, viene creata un'istanza della ViewRenderer classe , che a sua volta crea un'istanza di un controllo nativo UIView . Nella piattaforma Android la classe ViewRenderer crea un'istanza di un controllo View nativo. Nella piattaforma UWP (Universal Windows Platform) la classe ViewRenderer crea un'istanza di un controllo FrameworkElement nativo. Per altre informazioni sulle classi di controllo native e del renderer a cui Xamarin.Forms viene eseguito il mapping dei controlli, vedere Classi di base del renderer e controlli nativi.

Nota

Alcuni controlli in Android usano renderer veloci, che non usano la ViewRenderer classe . Per altre informazioni sui renderer veloci, vedere Xamarin.Forms Renderer veloci.

Il diagramma seguente illustra la relazione tra la classe View e i controlli nativi corrispondenti che la implementano:

Relazione tra la classe View e le classi native che la implementano

È possibile usare il processo di rendering per implementare personalizzazioni specifiche della piattaforma creando un renderer personalizzato per un elemento View in ogni piattaforma. Il processo per eseguire questa operazione è il seguente:

  1. Creare un Xamarin.Forms controllo personalizzato.
  2. Utilizzare il controllo personalizzato da Xamarin.Forms.
  3. Creare il renderer personalizzato per il controllo in ogni piattaforma.

Ogni elemento verrà trattato separatamente, per implementare un renderer CameraPreview che consente di visualizzare un'anteprima del flusso video dalla fotocamera del dispositivo. Toccare il flusso video per arrestarlo e avviarlo.

Creazione del controllo personalizzato

Per creare un controllo personalizzato, è possibile sottoclassare la classe View come illustrato nell'esempio di codice seguente:

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); }
  }
}

Il controllo personalizzato CameraPreview viene creato nel progetto di libreria .NET Standard e definisce l'API per il controllo. Il controllo personalizzato espone una proprietà Camera che viene usata per stabilire se il flusso video deve essere visualizzato dalla fotocamera anteriore o posteriore del dispositivo. Se non viene specificato un valore per la proprietà Camera quando viene creato il controllo, per impostazione predefinita viene specificata la fotocamera posteriore.

Uso del controllo personalizzato

Per fare riferimento al controllo personalizzato CameraPreview in XAML nel progetto di libreria .NET Standard, è possibile dichiarare uno spazio dei nomi per il percorso e usare il prefisso dello spazio dei nomi dell'elemento del controllo personalizzato. Nell'esempio di codice riportato di seguito viene illustrato come il controllo personalizzato CameraPreview può essere usato da una pagina XAML:

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

Il prefisso dello spazio dei nomi local può avere qualsiasi nome. I valori clr-namespace e assembly devono tuttavia corrispondere ai dettagli del controllo personalizzato. Dopo aver dichiarato lo spazio dei nomi, il prefisso viene usato per fare riferimento al controllo personalizzato.

Nell'esempio di codice riportato di seguito viene illustrato come il controllo personalizzato CameraPreview può essere usato da una pagina 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
        }
      }
    };
  }
}

Un'istanza del controllo personalizzato CameraPreview verrà usata per visualizzare il flusso video di anteprima dalla fotocamera del dispositivo. Oltre a specificare, se necessario, un valore per la proprietà Camera, la personalizzazione del controllo verrà eseguita nel renderer personalizzato.

Un renderer personalizzato può ora essere aggiunto a ogni progetto di applicazione per creare controlli di anteprima della fotocamera specifici della piattaforma.

Creazione del renderer personalizzato in ogni piattaforma

Il processo di creazione della classe renderer personalizzata in iOS e UWP è il seguente:

  1. Creare una sottoclasse della classe ViewRenderer<T1,T2> che esegue il rendering del controllo personalizzato. Il primo argomento tipo deve essere il controllo personalizzato per cui si usa il renderer, in questo caso CameraPreview. Il secondo argomento tipo deve essere il controllo nativo che implementerà il controllo personalizzato.
  2. Eseguire l'override del metodo OnElementChanged che esegue il rendering del controllo personalizzato e scrivere la logica per personalizzarlo. Questo metodo viene chiamato quando viene creato il controllo corrispondente Xamarin.Forms .
  3. Aggiungere un ExportRenderer attributo alla classe renderer personalizzata per specificare che verrà usato per eseguire il rendering del Xamarin.Forms controllo personalizzato. Questo attributo viene usato per registrare il renderer personalizzato con Xamarin.Forms.

Il processo di creazione della classe renderer personalizzata in Android, come renderer rapido, è il seguente:

  1. Creare una sottoclasse del controllo Android che esegue il rendering del controllo personalizzato. Specificare inoltre che la sottoclasse implementerà le IVisualElementRenderer interfacce e IViewRenderer .
  2. Implementare le IVisualElementRenderer interfacce e IViewRenderer nella classe renderer veloce.
  3. Aggiungere un ExportRenderer attributo alla classe renderer personalizzata per specificare che verrà usato per eseguire il rendering del Xamarin.Forms controllo personalizzato. Questo attributo viene usato per registrare il renderer personalizzato con Xamarin.Forms.

Nota

Per la maggior parte degli Xamarin.Forms elementi, è facoltativo fornire un renderer personalizzato in ogni progetto di piattaforma. Se un renderer personalizzato non è registrato, verrà usato il renderer predefinito per la classe di base del controllo. I renderer personalizzati sono tuttavia necessari in ogni progetto della piattaforma quando si esegue il rendering di un elemento View.

Il diagramma seguente illustra le responsabilità di ogni progetto nell'applicazione di esempio, insieme alle relazioni tra di essi:

Responsabilità del progetto di renderer personalizzato CameraPreview

Il rendering del CameraPreview controllo personalizzato viene eseguito da classi renderer specifiche della piattaforma, che derivano dalla ViewRenderer classe in iOS e UWP e dalla FrameLayout classe in Android. Di conseguenza il rendering di ogni controllo CameraPreview viene eseguito con controlli specifici della piattaforma, come illustrato negli screenshot seguenti:

CameraPreview in ogni piattaforma

La ViewRenderer classe espone il OnElementChanged metodo , che viene chiamato quando viene creato il controllo personalizzato per eseguire il Xamarin.Forms rendering del controllo nativo corrispondente. Questo metodo accetta un parametro ElementChangedEventArgs che contiene le proprietà OldElement e NewElement. Queste proprietà rappresentano l'elemento Xamarin.Forms a cui è stato associato il renderer e l'elemento Xamarin.Forms a cui è associato il renderer. Nell'applicazione di esempio la proprietà OldElement sarà null e la proprietà NewElement conterrà un riferimento all'istanza di CameraPreview.

La creazione dell'istanza e la personalizzazione del controllo nativo devono essere eseguite in una versione sostituita del metodo OnElementChanged in ogni classe di renderer specifica della piattaforma. Il metodo SetNativeControl deve essere usato per creare un'istanza del controllo nativo e assegna anche il riferimento al controllo alla proprietà Control. Inoltre, è possibile ottenere un riferimento al controllo di cui viene eseguito il Xamarin.Forms rendering tramite la Element proprietà .

In alcune circostanze il metodo OnElementChanged può essere chiamato più volte. Di conseguenza, per evitare perdite di memoria, prestare attenzione quando si crea un'istanza di un nuovo controllo nativo. L'approccio da usare quando si crea un'istanza di un nuovo controllo nativo in un renderer personalizzato è illustrato nell'esempio di codice seguente:

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
  }
}

Per un nuovo controllo nativo l'istanza deve essere creata solo una volta, quando la proprietà Control è null. Inoltre, il controllo deve essere creato, configurato e sottoscrittore solo i gestori eventi quando il renderer personalizzato è collegato a un nuovo Xamarin.Forms elemento. Analogamente, per i gestori degli eventi sottoscritti l'iscrizione deve essere annullata solo in caso di modifica dell'elemento a cui è allegato il renderer. L'adozione di questo approccio consente di creare un renderer personalizzato con prestazioni elevate che non subisce perdite di memoria.

Importante

Il metodo SetNativeControl deve essere chiamato solo se e.NewElement è diverso da null.

Ogni classe renderer personalizzata è decorata con un ExportRenderer attributo che registra il renderer con Xamarin.Forms. L'attributo accetta due parametri, ovvero il nome del tipo del controllo personalizzato di cui viene eseguito il Xamarin.Forms rendering e il nome del tipo del renderer personalizzato. Il prefisso assembly dell'attributo specifica che l'attributo viene applicato all'intero assembly.

Le sezioni seguenti illustrano l'implementazione della classe renderer personalizzato specifica di ogni piattaforma.

Creazione del renderer personalizzato in iOS

L'esempio di codice seguente illustra il renderer personalizzato per la piattaforma 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;
            }
        }
        ...
    }
}

Se la proprietà Control è null, viene chiamato il metodo SetNativeControl per creare un'istanza di un nuovo controllo UICameraPreview e assegnare un riferimento al controllo alla proprietà Control. Il controllo UICameraPreview è un controllo personalizzato specifico della piattaforma che usa le API AVCapture per definire il flusso di anteprima della fotocamera. Espone un evento Tapped gestito dal metodo OnCameraPreviewTapped per arrestare e avviare l'anteprima video quando viene toccata. L'evento Tapped viene sottoscritto quando il renderer personalizzato è associato a un nuovo Xamarin.Forms elemento e annulla la sottoscrizione solo quando l'elemento a cui è associato il renderer.

Creazione del renderer personalizzato in Android

L'esempio di codice seguente illustra il renderer rapido per la piattaforma 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;
            }
        }       
        // ...
    }
}

In questo esempio il OnElementChanged metodo crea un CameraFragment oggetto , purché il renderer personalizzato sia associato a un nuovo Xamarin.Forms elemento. Il CameraFragment tipo è una classe personalizzata che usa l'API Camera2 per fornire il flusso di anteprima dalla fotocamera. L'oggetto CameraFragment viene eliminato quando l'elemento a cui è associato il Xamarin.Forms renderer.

Creazione del renderer personalizzato in UWP

L'esempio di codice seguente illustra il renderer personalizzato per la piattaforma 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();
            }
        }
        ...
    }
}

A condizione che la proprietà Control sia null, viene creata un'istanza di un nuovo CaptureElement e viene chiamato il metodo SetupCamera, che usa l'API MediaCapture per specificare il flusso di anteprima della fotocamera. Viene quindi chiamato il metodo SetNativeControl per assegnare un riferimento all'istanza di CaptureElement alla proprietà Control. Il controllo CaptureElement espone un evento Tapped gestito dal metodo OnCameraPreviewTapped per arrestare e avviare l'anteprima video quando viene toccata. L'evento Tapped viene sottoscritto quando il renderer personalizzato è associato a un nuovo Xamarin.Forms elemento e annulla la sottoscrizione solo quando l'elemento a cui è associato il renderer.

Nota

In un'applicazione UWP è importante arrestare ed eliminare gli oggetti che consentono l'accesso alla fotocamera. Questi possono infatti interferire con altre applicazioni che tentano di accedere alla fotocamera del dispositivo. Per altre informazioni, vedere Display the camera preview (Visualizzare l'anteprima della fotocamera).

Riepilogo

Questo articolo ha illustrato come creare un renderer personalizzato per un Xamarin.Forms controllo personalizzato usato per visualizzare un flusso video di anteprima dalla fotocamera del dispositivo. Xamarin.Forms I controlli dell'interfaccia utente personalizzati devono derivare dalla View classe , usata per posizionare layout e controlli sullo schermo.