Implementace zobrazení
Xamarin.Forms Vlastní ovládací prvky uživatelského rozhraní by měly být odvozeny od třídy View, která slouží k umístění rozložení a ovládacích prvků na obrazovku. Tento článek ukazuje, jak vytvořit vlastní renderer pro Xamarin.Forms vlastní ovládací prvek, který se používá k zobrazení streamu videa ve verzi Preview z fotoaparátu zařízení.
Každé Xamarin.Forms zobrazení má doprovodný renderer pro každou platformu, která vytvoří instanci nativního ovládacího prvku. Když aplikace View
v iOSu Xamarin.Forms vykresluje aplikaci, ViewRenderer
vytvoří se instance třídy, která následně vytvoří instanci nativního UIView
ovládacího prvku. Na platformě Android třída ViewRenderer
vytvoří instanci nativního View
ovládacího prvku. V Univerzální platforma Windows (UPW) třída ViewRenderer
vytvoří instanci nativního FrameworkElement
ovládacího prvku. Další informace o rendereru a nativních tříd ovládacích prvcích, které Xamarin.Forms řídí mapování, naleznete v tématu Renderer Základní třídy a nativní ovládací prvky.
Poznámka:
Některé ovládací prvky v Androidu ViewRenderer
používají rychlé renderery, které třídu nespotřebovávají. Další informace o rychlých rendererech naleznete v tématu Xamarin.Forms Rychlé renderery.
Následující diagram znázorňuje vztah mezi View
a odpovídajícími nativními ovládacími prvky, které ho implementují:
Proces vykreslování lze použít k implementaci přizpůsobení specifických pro platformu vytvořením vlastního rendereru pro každou platformu View
. Postup je následující:
- Vytvořte Xamarin.Forms vlastní ovládací prvek.
- Využití vlastního ovládacího prvku z Xamarin.Forms.
- Vytvořte vlastní renderer pro ovládací prvek na každé platformě.
Každá položka se teď bude zabývat implementací rendereru CameraPreview
, který zobrazí stream videa ve verzi Preview z fotoaparátu zařízení. Klepnutím na stream videa ho zastavíte a spustíte.
Vytvoření vlastního ovládacího prvku
Vlastní ovládací prvek lze vytvořit podtřídou View
třídy, jak je znázorněno v následujícím příkladu kódu:
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); }
}
}
Vlastní CameraPreview
ovládací prvek se vytvoří v projektu knihovny .NET Standard a definuje rozhraní API pro ovládací prvek. Vlastní ovládací prvek zveřejňuje Camera
vlastnost, která se používá k řízení toho, jestli se má stream videa zobrazovat z přední nebo zadní kamery na zařízení. Pokud není pro Camera
vlastnost při vytváření ovládacího prvku zadána hodnota, ve výchozím nastavení určuje zadní kameru.
Využívání vlastního ovládacího prvku
Vlastní CameraPreview
ovládací prvek lze odkazovat v XAML v projektu knihovny .NET Standard deklarováním oboru názvů pro jeho umístění a použitím předpony oboru názvů u elementu vlastního ovládacího prvku. Následující příklad kódu ukazuje, jak CameraPreview
může vlastní ovládací prvek využívat stránka XAML:
<ContentPage ...
xmlns:local="clr-namespace:CustomRenderer;assembly=CustomRenderer"
...>
<StackLayout>
<Label Text="Camera Preview:" />
<local:CameraPreview Camera="Rear"
HorizontalOptions="FillAndExpand"
VerticalOptions="FillAndExpand" />
</StackLayout>
</ContentPage>
Předponu local
oboru názvů lze pojmenovat cokoli. clr-namespace
Hodnoty ale assembly
musí odpovídat podrobnostem vlastního ovládacího prvku. Jakmile je obor názvů deklarován, předpona se použije k odkazování na vlastní ovládací prvek.
Následující příklad kódu ukazuje, jak CameraPreview
může vlastní ovládací prvek využívat stránka jazyka 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
}
}
};
}
}
K zobrazení streamu videa ve verzi Preview z fotoaparátu zařízení se použije instance vlastního CameraPreview
ovládacího prvku. Kromě volitelného zadání hodnoty vlastnosti Camera
se přizpůsobení ovládacího prvku provede ve vlastním rendereru.
Do každého projektu aplikace je teď možné přidat vlastní renderer, který vytvoří ovládací prvky náhledu kamery specifické pro platformu.
Vytvoření vlastního rendereru na jednotlivých platformách
Proces vytvoření vlastní třídy rendereru v iOSu a UPW je následující:
- Vytvořte podtřídu
ViewRenderer<T1,T2>
třídy, která vykreslí vlastní ovládací prvek. Prvním argumentem typu by měl být vlastní ovládací prvek, pro který je renderer v tomto případěCameraPreview
. Druhým argumentem typu by měl být nativní ovládací prvek, který bude implementovat vlastní ovládací prvek. - Přepište metodu
OnElementChanged
, která vykreslí vlastní ovládací prvek a zapisuje logiku pro přizpůsobení. Tato metoda je volána při vytvoření odpovídajícího Xamarin.Forms ovládacího prvku. - Přidejte do vlastní třídy rendereru
ExportRenderer
atribut, který určí, že se použije k vykreslení vlastního Xamarin.Forms ovládacího prvku. Tento atribut se používá k registraci vlastního rendereru v Xamarin.Forms.
Proces vytvoření vlastní třídy rendereru v Androidu jako rychlého rendereru je následující:
- Vytvořte podtřídu ovládacího prvku Android, který vykreslí vlastní ovládací prvek. Kromě toho určete, že podtřída bude implementovat
IVisualElementRenderer
rozhraní aIViewRenderer
rozhraní. - Implementujte rozhraní
IVisualElementRenderer
IViewRenderer
ve třídě rychlého rendereru. - Přidejte do vlastní třídy rendereru
ExportRenderer
atribut, který určí, že se použije k vykreslení vlastního Xamarin.Forms ovládacího prvku. Tento atribut se používá k registraci vlastního rendereru v Xamarin.Forms.
Poznámka:
Pro většinu Xamarin.Forms prvků je volitelné poskytnout vlastní renderer v každém projektu platformy. Pokud není zaregistrovaný vlastní renderer, použije se výchozí renderer základní třídy ovládacího prvku. Vlastní renderery jsou však vyžadovány v každém projektu platformy při vykreslování prvku View .
Následující diagram znázorňuje zodpovědnosti jednotlivých projektů v ukázkové aplikaci spolu s relacemi mezi nimi:
Vlastní CameraPreview
ovládací prvek se vykresluje pomocí tříd rendererů specifických pro platformu, které jsou odvozeny od ViewRenderer
třídy v systémech iOS a UPW a z třídy v Androidu FrameLayout
. Výsledkem je vykreslení každého CameraPreview
vlastního ovládacího prvku pomocí ovládacích prvků specifických pro platformu, jak je znázorněno na následujících snímcích obrazovky:
Třída ViewRenderer
zveřejňuje metodu OnElementChanged
, která je volána při Xamarin.Forms vytvoření vlastního ovládacího prvku pro vykreslení odpovídajícího nativního ovládacího prvku. Tato metoda přebírá ElementChangedEventArgs
parametr, který obsahuje OldElement
a NewElement
vlastnosti. Tyto vlastnosti představují Xamarin.Forms prvek, ke kterému byl renderer připojen, a Xamarin.Forms prvek, ke kterému je renderer připojen, v uvedeném pořadí. V ukázkové aplikaci OldElement
bude vlastnost a NewElement
vlastnost bude obsahovat odkaz na CameraPreview
null
instanci.
Přepsáná verze OnElementChanged
metody v každé třídě rendereru specifické pro platformu je místo k provedení instance a přizpůsobení nativního řízení. Metoda SetNativeControl
by měla být použita k vytvoření instance nativního ovládacího prvku, a tato metoda také přiřadí odkaz na ovládací prvek vlastnosti Control
. Kromě toho lze prostřednictvím vlastnosti získat Element
odkaz na Xamarin.Forms vykreslovaný ovládací prvek.
V některých případech lze metodu OnElementChanged
volat vícekrát. Aby se zabránilo nevrácené paměti, je třeba při vytváření instance nového nativního ovládacího prvku věnovat pozornost. Přístup k použití při vytváření instance nového nativního ovládacího prvku ve vlastním rendereru se zobrazí v následujícím příkladu kódu:
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
}
}
Nový nativní ovládací prvek by měl být vytvořena pouze jednou, pokud Control
je null
vlastnost . Kromě toho by měl být ovládací prvek vytvořen, nakonfigurován a obslužné rutiny událostí odebírané, když je vlastní renderer připojen k novému Xamarin.Forms prvku. Podobně by všechny obslužné rutiny událostí, které byly přihlášeny k odběru, měly být odhlášené pouze tehdy, když je prvek, který renderer připojí ke změnám. Přijetí tohoto přístupu vám pomůže vytvořit výkonný vlastní renderer, který netrácí paměť.
Důležité
Metoda SetNativeControl
by měla být volána pouze v případě e.NewElement
, že není null
.
Každá vlastní třída rendereru je zdobena ExportRenderer
atributem, který registruje renderer s Xamarin.Forms. Atribut má dva parametry – název typu vykreslovaného vlastního Xamarin.Forms ovládacího prvku a název typu vlastního rendereru. Předpona assembly
atributu určuje, že atribut se vztahuje na celé sestavení.
Následující části se týkají implementace jednotlivých vlastních tříd rendereru specifických pro platformu.
Vytvoření vlastního rendereru v iOSu
Následující příklad kódu ukazuje vlastní renderer pro platformu 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;
}
}
...
}
}
Za předpokladu Control
SetNativeControl
, že vlastnost je null
, metoda je volána vytvořit instanci nového UICameraPreview
ovládacího prvku a přiřadit odkaz na Control
vlastnost. Ovládací UICameraPreview
prvek je vlastní ovládací prvek specifický pro platformu, který používá AVCapture
rozhraní API k poskytování streamu náhledu z fotoaparátu. Zobrazí Tapped
událost, která je zpracována metodou OnCameraPreviewTapped
, aby se zastavila a spustila náhled videa, když na ni klepnete. Událost Tapped
se přihlásí k odběru, když je vlastní renderer připojený k novému Xamarin.Forms prvku a odhlásí se pouze v případě, že je renderer připojený ke změnám.
Vytvoření vlastního rendereru v Androidu
Následující příklad kódu ukazuje rychlý renderer pro platformu 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;
}
}
// ...
}
}
V tomto příkladu OnElementChanged
metoda vytvoří CameraFragment
objekt za předpokladu, že vlastní renderer je připojen k novému Xamarin.Forms prvku. Typ CameraFragment
je vlastní třída, která používá Camera2
rozhraní API k poskytnutí streamu náhledu z fotoaparátu. Objekt CameraFragment
je uvolněn, když Xamarin.Forms prvek renderer je připojen ke změnám.
Vytvoření vlastního rendereru v UPW
Následující příklad kódu ukazuje vlastní renderer pro UPW:
[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();
}
}
...
}
}
Za předpokladu Control
, že vlastnost je null
, je vytvořena nová CaptureElement
instance a SetupCamera
volá se metoda, která používá MediaCapture
rozhraní API k poskytnutí streamu náhledu z fotoaparátu. Metoda SetNativeControl
je pak volána pro přiřazení odkazu na CaptureElement
instanci k Control
vlastnosti. Ovládací CaptureElement
prvek zveřejňuje Tapped
událost, která je zpracována metodou OnCameraPreviewTapped
, aby se zastavila a spustila náhled videa po klepnutí. Událost Tapped
se přihlásí k odběru, když je vlastní renderer připojený k novému Xamarin.Forms prvku a odhlásí se pouze v případě, že je renderer připojený ke změnám.
Poznámka:
Je důležité zastavit a odstranit objekty, které poskytují přístup ke kameře v aplikaci pro UPW. Pokud to neuděláte, může kolidovat s jinými aplikacemi, které se pokusí o přístup ke kameře zařízení. Další informace najdete v tématu Zobrazení náhledu fotoaparátu.
Shrnutí
Tento článek ukazuje, jak vytvořit vlastní renderer pro Xamarin.Forms vlastní ovládací prvek, který se používá k zobrazení streamu videa ve verzi Preview z fotoaparátu zařízení. Xamarin.Forms vlastní ovládací prvky uživatelského rozhraní by měly být odvozeny od View
třídy, která se používá k umístění rozložení a ovládacích prvků na obrazovku.