Udostępnij za pośrednictwem


Implementowanie kontrolki View

Xamarin.Forms niestandardowe kontrolki interfejsu użytkownika powinny pochodzić z klasy View, która służy do umieszczania układów i kontrolek na ekranie. W tym artykule pokazano, jak utworzyć niestandardowy moduł renderujący dla niestandardowej Xamarin.Forms kontrolki używanej do wyświetlania podglądu strumienia wideo z aparatu urządzenia.

Każdy Xamarin.Forms widok ma towarzyszący moduł renderujący dla każdej platformy, który tworzy wystąpienie kontrolki natywnej. Gdy element View jest renderowany przez aplikację Xamarin.Forms w systemie iOS, ViewRenderer klasę tworzy wystąpienie, co z kolei tworzy wystąpienie kontrolki natywnej UIView . Na platformie ViewRenderer Android klasa tworzy wystąpienie natywnej View kontrolki. W platforma uniwersalna systemu Windows (UWP) ViewRenderer klasa tworzy wystąpienie kontrolki natywnejFrameworkElement. Aby uzyskać więcej informacji na temat klasy renderera i natywnych kontrolek mapowanych Xamarin.Forms na, zobacz Renderer Base Classes and Native Controls (Klasy bazowe modułu renderowania i kontrolki natywne).

Uwaga

Niektóre kontrolki w systemie Android używają szybkich modułów renderujących, które nie korzystają z ViewRenderer klasy. Aby uzyskać więcej informacji na temat szybkich modułów renderujących, zobacz Xamarin.Forms Fast Renderers (Szybkie programy renderowania).

Na poniższym diagramie przedstawiono relację między kontrolkami View macierzystymi i odpowiadającymi im kontrolkami natywnymi, które je implementują:

Relacja między klasą widoków a jej implementacją klas natywnych

Proces renderowania może służyć do implementowania dostosowań specyficznych dla platformy przez utworzenie niestandardowego modułu renderowania dla elementu na View każdej platformie. Proces wykonywania tej czynności jest następujący:

  1. Utwórz kontrolkę niestandardową Xamarin.Forms .
  2. Zużyj kontrolkę niestandardową z poziomu Xamarin.Forms.
  3. Utwórz niestandardowy moduł renderowania dla kontrolki na każdej platformie.

Każdy element zostanie omówiony z kolei, aby zaimplementować CameraPreview program renderujący wyświetlający podgląd strumienia wideo z aparatu urządzenia. Naciśnięcie strumienia wideo spowoduje zatrzymanie i uruchomienie go.

Tworzenie kontrolki niestandardowej

Kontrolkę niestandardową można utworzyć przez podklasę View klasy, jak pokazano w poniższym przykładzie kodu:

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

Kontrolka niestandardowa CameraPreview jest tworzona w projekcie biblioteki .NET Standard i definiuje interfejs API dla kontrolki. Kontrolka niestandardowa uwidacznia Camera właściwość używaną do kontrolowania, czy strumień wideo powinien być wyświetlany z przedniej lub tylnej kamery na urządzeniu. Jeśli wartość nie jest określona dla Camera właściwości podczas tworzenia kontrolki, domyślnie określa kamerę tylną.

Korzystanie z kontrolki niestandardowej

Do CameraPreview kontrolki niestandardowej można odwoływać się w języku XAML w projekcie biblioteki .NET Standard, deklarując przestrzeń nazw dla swojej lokalizacji i używając prefiksu przestrzeni nazw w niestandardowym elemencie kontrolki. Poniższy przykład kodu przedstawia sposób CameraPreview korzystania z kontrolki niestandardowej przez stronę XAML:

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

Prefiks local przestrzeni nazw może mieć nazwę dowolnych elementów. clr-namespace Jednak wartości i assembly muszą być zgodne ze szczegółami kontrolki niestandardowej. Po zadeklarowaniu przestrzeni nazw prefiks jest używany do odwołwania się do kontrolki niestandardowej.

Poniższy przykład kodu pokazuje, jak kontrolka CameraPreview niestandardowa może być zużywana przez stronę języka 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
        }
      }
    };
  }
}

Wystąpienie kontrolki niestandardowej CameraPreview będzie używane do wyświetlania podglądu strumienia wideo z aparatu urządzenia. Oprócz opcjonalnego określenia wartości dla Camera właściwości dostosowanie kontrolki zostanie przeprowadzone w niestandardowym module renderowania.

Niestandardowy moduł renderowania można teraz dodać do każdego projektu aplikacji w celu utworzenia kontrolek podglądu aparatu specyficznego dla platformy.

Tworzenie niestandardowego modułu renderowania na każdej platformie

Proces tworzenia niestandardowej klasy renderera w systemach iOS i UWP jest następujący:

  1. Utwórz podklasę ViewRenderer<T1,T2> klasy, która renderuje kontrolkę niestandardową. Pierwszym argumentem typu powinna być kontrolka niestandardowa, dla których jest moduł renderowany, w tym przypadku CameraPreview. Drugi argument typu powinien być natywną kontrolką, która będzie implementować kontrolkę niestandardową.
  2. Zastąpi metodę OnElementChanged , która renderuje kontrolkę niestandardową i zapisuje logikę, aby ją dostosować. Ta metoda jest wywoływana po utworzeniu odpowiedniej Xamarin.Forms kontrolki.
  3. ExportRenderer Dodaj atrybut do niestandardowej klasy renderera, aby określić, że będzie on używany do renderowania kontrolki niestandardowejXamarin.Forms. Ten atrybut służy do rejestrowania niestandardowego modułu renderowania za pomocą Xamarin.Formspolecenia .

Proces tworzenia niestandardowej klasy modułu renderowania w systemie Android jako szybkiego renderowania jest następujący:

  1. Utwórz podklasę kontrolki systemu Android, która renderuje kontrolkę niestandardową. Ponadto określ, że podklasa będzie implementować IVisualElementRenderer interfejsy i IViewRenderer .
  2. Zaimplementuj IVisualElementRenderer interfejsy i IViewRenderer w klasie szybkiego renderowania.
  3. ExportRenderer Dodaj atrybut do niestandardowej klasy renderera, aby określić, że będzie on używany do renderowania kontrolki niestandardowejXamarin.Forms. Ten atrybut służy do rejestrowania niestandardowego modułu renderowania za pomocą Xamarin.Formspolecenia .

Uwaga

W przypadku większości Xamarin.Forms elementów opcjonalne jest udostępnienie niestandardowego modułu renderowania w każdym projekcie platformy. Jeśli niestandardowy moduł renderowania nie jest zarejestrowany, zostanie użyty domyślny moduł renderowania dla klasy bazowej kontrolki. Jednak niestandardowe programy renderowane są wymagane w każdym projekcie platformy podczas renderowania elementu View .

Na poniższym diagramie przedstawiono obowiązki każdego projektu w przykładowej aplikacji wraz z relacjami między nimi:

Aparat Pogląd niestandardowych obowiązków projektu renderowania

Kontrolka niestandardowa CameraPreview jest renderowana przez klasy renderer specyficzne dla platformy, które pochodzą z ViewRenderer klasy w systemach iOS i UWP oraz z FrameLayout klasy w systemie Android. Powoduje to renderowanie każdej CameraPreview niestandardowej kontrolki z kontrolkami specyficznymi dla platformy, jak pokazano na poniższych zrzutach ekranu:

Aparat Przegląd na każdej platformie

Klasa ViewRenderer uwidacznia metodę OnElementChanged , która jest wywoływana podczas tworzenia niestandardowej Xamarin.Forms kontrolki w celu renderowania odpowiedniej kontrolki natywnej. Ta metoda przyjmuje ElementChangedEventArgs parametr zawierający OldElement właściwości i NewElement . Te właściwości reprezentują Xamarin.Forms element dołączony do modułu renderowania, a Xamarin.Forms element, do którego jest dołączony moduł renderujący. W przykładowej aplikacji OldElement właściwość będzie mieć null wartość , a NewElement właściwość będzie zawierać odwołanie do CameraPreview wystąpienia.

Zastąpiona wersja metody , w każdej klasie modułu OnElementChanged renderowania specyficznego dla platformy, to miejsce do wykonania wystąpienia i dostosowania natywnej kontrolki. Metoda SetNativeControl powinna służyć do utworzenia wystąpienia kontrolki natywnej, a ta metoda przypisze również odwołanie do kontrolki Control do właściwości . Ponadto można uzyskać odwołanie do Xamarin.Forms renderowanej kontrolki Element za pośrednictwem właściwości .

W niektórych okolicznościach metoda może być wywoływana OnElementChanged wiele razy. W związku z tym, aby zapobiec wyciekom pamięci, należy zachować ostrożność podczas tworzenia wystąpienia nowej natywnej kontrolki. Podejście do użycia podczas tworzenia wystąpienia nowej natywnej kontrolki w niestandardowym programie renderującym jest pokazane w poniższym przykładzie kodu:

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

Nową kontrolkę natywną należy utworzyć tylko raz, gdy Control właściwość ma wartość null. Ponadto kontrolka powinna być tworzona, konfigurowana i subskrybowana tylko po dołączeniu niestandardowego modułu renderowania do nowego Xamarin.Forms elementu. Podobnie wszystkie programy obsługi zdarzeń, które zostały zasubskrybowane, powinny być anulowane tylko wtedy, gdy element renderujący jest dołączony do zmian. Wdrożenie tego podejścia pomoże utworzyć wydajny niestandardowy moduł renderujący, który nie cierpi na przecieki pamięci.

Ważne

Metoda powinna być wywoływana SetNativeControl tylko wtedy, gdy e.NewElement nie nulljest .

Każda niestandardowa klasa modułu renderowania jest ozdobiona atrybutem ExportRenderer , który rejestruje program renderujący za pomocą Xamarin.Formspolecenia . Atrybut przyjmuje dwa parametry — nazwę Xamarin.Forms typu renderowanej kontrolki niestandardowej i nazwę typu niestandardowego modułu renderowania. Prefiks assembly atrybutu określa, że atrybut ma zastosowanie do całego zestawu.

W poniższych sekcjach omówiono implementację poszczególnych niestandardowych klas renderer specyficznych dla platformy.

Tworzenie niestandardowego modułu renderowania w systemie iOS

Poniższy przykład kodu przedstawia niestandardowy moduł renderowania dla platformy 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;
            }
        }
        ...
    }
}

Pod warunkiem, że Control właściwość to null, SetNativeControl metoda jest wywoływana w celu utworzenia wystąpienia nowej UICameraPreview kontrolki i przypisania odwołania do niej do Control właściwości. Kontrolka to niestandardowa kontrolka UICameraPreview specyficzna dla platformy, która używa AVCapture interfejsów API do udostępniania strumienia podglądu z aparatu. Uwidacznia Tapped zdarzenie obsługiwane przez OnCameraPreviewTapped metodę w celu zatrzymania i uruchomienia podglądu wideo po naciśnięciu. Zdarzenie Tapped jest subskrybowane po dołączeniu niestandardowego modułu renderowania do nowego Xamarin.Forms elementu i anulowaniu subskrypcji tylko wtedy, gdy element renderator jest dołączony do zmian.

Tworzenie niestandardowego modułu renderowania w systemie Android

Poniższy przykład kodu przedstawia szybki moduł renderowania dla platformy 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;
            }
        }       
        // ...
    }
}

W tym przykładzie OnElementChanged metoda tworzy CameraFragment obiekt, pod warunkiem, że niestandardowy moduł renderujący jest dołączony do nowego Xamarin.Forms elementu. Typ CameraFragment to klasa niestandardowa, która używa interfejsu Camera2 API do udostępniania strumienia podglądu z aparatu. Obiekt CameraFragment jest usuwany, gdy Xamarin.Forms element renderator jest dołączony do zmian.

Tworzenie niestandardowego modułu renderowania na platformie UWP

W poniższym przykładzie kodu pokazano niestandardowy moduł renderowania dla platformy 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();
            }
        }
        ...
    }
}

Pod warunkiem, że Control właściwość to null, jest tworzone wystąpienie SetupCamera nowego CaptureElement i wywoływana jest metoda , która używa interfejsu MediaCapture API do udostępniania strumienia podglądu z aparatu. Następnie wywoływana SetNativeControl jest metoda w celu przypisania odwołania do CaptureElement wystąpienia do Control właściwości . Kontrolka CaptureElement uwidacznia Tapped zdarzenie obsługiwane przez OnCameraPreviewTapped metodę w celu zatrzymania i uruchomienia podglądu wideo po jej naciśnięciu. Zdarzenie Tapped jest subskrybowane po dołączeniu niestandardowego modułu renderowania do nowego Xamarin.Forms elementu i anulowaniu subskrypcji tylko wtedy, gdy element renderator jest dołączony do zmian.

Uwaga

Ważne jest, aby zatrzymać i usunąć obiekty, które zapewniają dostęp do aparatu w aplikacji platformy UWP. Niepowodzenie tej czynności może zakłócać działanie innych aplikacji, które próbują uzyskać dostęp do aparatu urządzenia. Aby uzyskać więcej informacji, zobacz Wyświetlanie podglądu aparatu.

Podsumowanie

W tym artykule pokazano, jak utworzyć niestandardowy moduł renderujący dla niestandardowej Xamarin.Forms kontrolki używanej do wyświetlania strumienia wideo w wersji zapoznawczej z aparatu urządzenia. Xamarin.Forms niestandardowe kontrolki interfejsu użytkownika powinny pochodzić z View klasy , która służy do umieszczania układów i kontrolek na ekranie.