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:
È 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:
- Creare un Xamarin.Forms controllo personalizzato.
- Utilizzare il controllo personalizzato da Xamarin.Forms.
- 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:
- 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 casoCameraPreview
. Il secondo argomento tipo deve essere il controllo nativo che implementerà il controllo personalizzato. - 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 . - 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:
- Creare una sottoclasse del controllo Android che esegue il rendering del controllo personalizzato. Specificare inoltre che la sottoclasse implementerà le
IVisualElementRenderer
interfacce eIViewRenderer
. - Implementare le
IVisualElementRenderer
interfacce eIViewRenderer
nella classe renderer veloce. - 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:
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:
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.