Sdílet prostřednictvím


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í:

Vztah mezi třídou view a její implementací nativních tříd

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í:

  1. Vytvořte Xamarin.Forms vlastní ovládací prvek.
  2. Využití vlastního ovládacího prvku z Xamarin.Forms.
  3. 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í:

  1. 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.
  2. 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.
  3. 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í:

  1. 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í a IViewRenderer rozhraní.
  2. Implementujte rozhraní IVisualElementRenderer IViewRenderer ve třídě rychlého rendereru.
  3. 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:

Odpovědnosti vlastního vykreslovacího projektu CameraPreview

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:

CameraPreview na jednotlivých platformách

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 nullvlastnost . 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.