Sdílet prostřednictvím


Vytvoření vlastního ovládacího prvku pomocí obslužných rutin

Browse sample. Procházení ukázky

Standardním požadavkem pro aplikace je možnost přehrávat videa. Tento článek popisuje, jak vytvořit multiplatformní uživatelské rozhraní multiplatformních Video aplikací .NET MAUI (.NET MAUI), které používá obslužnou rutinu k mapování rozhraní API pro řízení napříč platformami na nativní zobrazení v androidu, iOSu a Mac Catalystu, která přehrávají videa. Tento ovládací prvek může přehrávat video ze tří zdrojů:

  • Adresa URL, která představuje vzdálené video.
  • Prostředek, což je soubor vložený do aplikace.
  • Soubor z knihovny videí zařízení.

Ovládací prvky videa vyžadují ovládací prvky přenosu, což jsou tlačítka pro přehrávání a pozastavení videa, a panel umístění, který ukazuje průběh videa a umožňuje uživateli rychle přejít na jiné místo. Ovládací Video prvek může buď použít ovládací prvky přenosu a poziční pruh poskytovaný platformou, nebo můžete zadat vlastní ovládací prvky přenosu a poziční pruh. Následující snímky obrazovky ukazují ovládací prvek v iOSu s vlastními ovládacími prvky přenosu a bez těchto ovládacích prvků:

Screenshot of video playback on iOS.Screenshot of video playback using custom transport controls on iOS.

Sofistikovanější ovládací prvek videa by měl další funkce, jako je například ovládací prvek hlasitosti, mechanismus přerušení přehrávání videa při přijetí hovoru a způsob, jak během přehrávání zachovat aktivní obrazovku.

Architektura Video ovládacího prvku je znázorněna v následujícím diagramu:

Video handler architecture.

Třída Video poskytuje rozhraní API pro různé platformy pro ovládací prvek. Mapování rozhraní API pro různé platformy na nativní rozhraní API zobrazení provádí VideoHandler třída na každé platformě, která mapuje Video třídu na MauiVideoPlayer třídu. Na iOS a Mac Catalyst třída MauiVideoPlayer používá AVPlayer typ k poskytování přehrávání videa. V Androidu třída MauiVideoPlayer používá VideoView typ k poskytování přehrávání videa. Ve Windows třída MauiVideoPlayer používá MediaPlayerElement typ k poskytování přehrávání videa.

Důležité

.NET MAUI odděluje své obslužné rutiny od svých ovládacích prvků napříč platformami prostřednictvím rozhraní. To umožňuje experimentálním architekturám, jako je Comet a Fabulous, poskytovat vlastní multiplatformní ovládací prvky, které implementují rozhraní, a přitom stále používají obslužné rutiny .NET MAUI. Vytvoření rozhraní pro řízení napříč platformami je nutné pouze v případě, že potřebujete oddělit obslužnou rutinu od jeho řízení pro různé platformy pro podobný účel nebo pro účely testování.

Proces vytvoření vlastního ovládacího prvku .NET MAUI pro různé platformy, jehož implementace platformy jsou poskytovány obslužnými rutinami, je následující:

  1. Vytvořte třídu pro multiplatformní řízení, která poskytuje veřejné rozhraní API ovládacího prvku. Další informace najdete v tématu Vytvoření víceplatformového ovládacího prvku.
  2. Vytvořte všechny požadované další typy napříč platformami.
  3. Vytvořte partial třídu obslužné rutiny. Další informace naleznete v tématu Vytvoření obslužné rutiny.
  4. Ve třídě obslužné rutiny vytvořte PropertyMapper slovník, který definuje akce, které se mají provést při změně vlastností pro různé platformy. Další informace naleznete v tématu Vytvoření mapovače vlastností.
  5. Volitelně ve třídě obslužné rutiny vytvořte CommandMapper slovník, který definuje akce, které se mají provést, když ovládací prvek pro různé platformy odesílá instrukce do nativních zobrazení, která implementují řízení napříč platformami. Další informace najdete v tématu Vytvoření mapperu příkazů.
  6. Vytvořte partial třídy obslužné rutiny pro každou platformu, která vytváří nativní zobrazení, která implementují řízení napříč platformami. Další informace najdete v tématu Vytvoření ovládacích prvků platformy.
  7. Zaregistrujte obslužnou rutinu pomocí ConfigureMauiHandlers metod a AddHandler metod ve třídě vaší aplikace MauiProgram . Další informace naleznete v tématu Registrace obslužné rutiny.

Pak je možné využívat řízení napříč platformami. Další informace najdete v tématu Využití řízení napříč platformami.

Vytvoření ovládacího prvku pro různé platformy

Pokud chcete vytvořit ovládací prvek pro různé platformy, měli byste vytvořit třídu odvozenou z View:

using System.ComponentModel;

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        public static readonly BindableProperty AreTransportControlsEnabledProperty =
            BindableProperty.Create(nameof(AreTransportControlsEnabled), typeof(bool), typeof(Video), true);

        public static readonly BindableProperty SourceProperty =
            BindableProperty.Create(nameof(Source), typeof(VideoSource), typeof(Video), null);

        public static readonly BindableProperty AutoPlayProperty =
            BindableProperty.Create(nameof(AutoPlay), typeof(bool), typeof(Video), true);

        public static readonly BindableProperty IsLoopingProperty =
            BindableProperty.Create(nameof(IsLooping), typeof(bool), typeof(Video), false);            

        public bool AreTransportControlsEnabled
        {
            get { return (bool)GetValue(AreTransportControlsEnabledProperty); }
            set { SetValue(AreTransportControlsEnabledProperty, value); }
        }

        [TypeConverter(typeof(VideoSourceConverter))]
        public VideoSource Source
        {
            get { return (VideoSource)GetValue(SourceProperty); }
            set { SetValue(SourceProperty, value); }
        }

        public bool AutoPlay
        {
            get { return (bool)GetValue(AutoPlayProperty); }
            set { SetValue(AutoPlayProperty, value); }
        }

        public bool IsLooping
        {
            get { return (bool)GetValue(IsLoopingProperty); }
            set { SetValue(IsLoopingProperty, value); }
        }        
        ...
    }
}

Ovládací prvek by měl poskytovat veřejné rozhraní API, ke kterému bude přistupovat jeho obslužná rutina, a řídit uživatele. Ovládací prvky napříč platformami by měly být odvozeny od View, který představuje vizuální prvek, který se používá k umístění rozložení a zobrazení na obrazovce.

Vytvoření obslužné rutiny

Po vytvoření řízení napříč platformami byste měli vytvořit třídu pro obslužnou rutinu partial :

#if IOS || MACCATALYST
using PlatformView = VideoDemos.Platforms.MaciOS.MauiVideoPlayer;
#elif ANDROID
using PlatformView = VideoDemos.Platforms.Android.MauiVideoPlayer;
#elif WINDOWS
using PlatformView = VideoDemos.Platforms.Windows.MauiVideoPlayer;
#elif (NETSTANDARD || !PLATFORM) || (NET6_0_OR_GREATER && !IOS && !ANDROID)
using PlatformView = System.Object;
#endif
using VideoDemos.Controls;
using Microsoft.Maui.Handlers;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler
    {
    }
}

Třída obslužné rutiny je částečná třída, jejíž implementace bude dokončena na každé platformě s další částečnou třídou.

Podmíněné using příkazy definují PlatformView typ na každé platformě. V systémech Android, iOS, Mac Catalyst a Windows jsou nativní zobrazení poskytována vlastní MauiVideoPlayer třídou. Konečný podmíněný using příkaz definuje PlatformView , že se System.Objectrovná . To je nezbytné, aby se PlatformView typ mohl použít v rámci obslužné rutiny pro použití na všech platformách. Alternativou by bylo definovat vlastnost jednou pro každou platformu PlatformView pomocí podmíněné kompilace.

Vytvoření mapovače vlastností

Každá obslužná rutina obvykle poskytuje mapovač vlastností, který definuje, jaké akce se mají provést, když dojde ke změně vlastnosti v ovládacím prvku pro různé platformy. Typ PropertyMapper je typ Dictionary , který mapuje vlastnosti ovládacího prvku pro různé platformy na přidružené akce.

PropertyMapper je definován v obecné ViewHandler třídě .NET MAUI a vyžaduje, aby byly zadány dva obecné argumenty:

  • Třída pro řízení napříč platformami, která je odvozena od View.
  • Třída obslužné rutiny.

Následující příklad kódu ukazuje VideoHandler třídu rozšířenou o definici PropertyMapper :

public partial class VideoHandler
{
    public static IPropertyMapper<Video, VideoHandler> PropertyMapper = new PropertyMapper<Video, VideoHandler>(ViewHandler.ViewMapper)
    {
        [nameof(Video.AreTransportControlsEnabled)] = MapAreTransportControlsEnabled,
        [nameof(Video.Source)] = MapSource,
        [nameof(Video.IsLooping)] = MapIsLooping,
        [nameof(Video.Position)] = MapPosition
    };

    public VideoHandler() : base(PropertyMapper)
    {
    }
}

Jedná PropertyMapper se o Dictionary klíč, jehož klíč je string a jehož hodnota je obecný Action. Představuje string název vlastnosti ovládacího prvku pro různé platformy a Action představuje metodu static , která vyžaduje obslužnou rutinu a řízení mezi platformami jako argumenty. Například podpis MapSource metody je public static void MapSource(VideoHandler handler, Video video).

Každá obslužná rutina platformy musí poskytovat implementace akcí, které manipulují s rozhraními API nativního zobrazení. Tím se zajistí, že když je vlastnost nastavena v ovládacím prvku pro různé platformy, podkladové nativní zobrazení se podle potřeby aktualizuje. Výhodou tohoto přístupu je, že umožňuje snadné přizpůsobení řízení napříč platformami, protože mapovač vlastností může být upraven spotřebiteli řízení napříč platformami bez podtřídy.

Vytvoření mapovače příkazů

Každá obslužná rutina může také poskytnout mapovač příkazů, který definuje, jaké akce se mají provést, když ovládací prvek pro různé platformy odesílá příkazy do nativních zobrazení. Mapovače příkazů jsou podobné mapovačům vlastností, ale umožňují předání dalších dat. V tomto kontextu je příkaz instrukce a volitelně jeho data, která se odesílají do nativního zobrazení. Typ CommandMapper je typ Dictionary , který mapuje členy řízení napříč platformami na jejich přidružené akce.

CommandMapper je definován v obecné ViewHandler třídě .NET MAUI a vyžaduje, aby byly zadány dva obecné argumenty:

  • Třída pro řízení napříč platformami, která je odvozena od View.
  • Třída obslužné rutiny.

Následující příklad kódu ukazuje VideoHandler třídu rozšířenou o definici CommandMapper :

public partial class VideoHandler
{
    public static IPropertyMapper<Video, VideoHandler> PropertyMapper = new PropertyMapper<Video, VideoHandler>(ViewHandler.ViewMapper)
    {
        [nameof(Video.AreTransportControlsEnabled)] = MapAreTransportControlsEnabled,
        [nameof(Video.Source)] = MapSource,
        [nameof(Video.IsLooping)] = MapIsLooping,
        [nameof(Video.Position)] = MapPosition
    };

    public static CommandMapper<Video, VideoHandler> CommandMapper = new(ViewCommandMapper)
    {
        [nameof(Video.UpdateStatus)] = MapUpdateStatus,
        [nameof(Video.PlayRequested)] = MapPlayRequested,
        [nameof(Video.PauseRequested)] = MapPauseRequested,
        [nameof(Video.StopRequested)] = MapStopRequested
    };

    public VideoHandler() : base(PropertyMapper, CommandMapper)
    {
    }
}

Jedná CommandMapper se o Dictionary klíč, jehož klíč je string a jehož hodnota je obecný Action. Představuje string název příkazu ovládacího prvku pro různé platformy a Action představuje metodu, která vyžaduje obslužnou static rutinu, řízení mezi platformami a volitelná data jako argumenty. Například podpis MapPlayRequested metody je public static void MapPlayRequested(VideoHandler handler, Video video, object? args).

Každá obslužná rutina platformy musí poskytovat implementace akcí, které manipulují s rozhraními API nativního zobrazení. Tím se zajistí, že když se příkaz odešle z ovládacího prvku pro různé platformy, bude s podkladovým nativním zobrazením manipulováno podle potřeby. Výhodou tohoto přístupu je, že eliminuje potřebu nativních zobrazení k odběru a odhlášení odběru událostí řízení napříč platformami. Kromě toho umožňuje snadné přizpůsobení, protože mapovač příkazů je možné upravit příjemci řízení napříč platformami bez podtřídy.

Vytvoření ovládacích prvků platformy

Po vytvoření mapovačů pro obslužnou rutinu musíte poskytnout implementace obslužné rutiny na všech platformách. Toho lze dosáhnout přidáním dílčích implementací obslužné rutiny tříd do podřízených složek složky Platformy . Případně můžete projekt nakonfigurovat tak, aby podporoval cílení na více názvů souborů nebo více cílení na složky nebo obojí.

Ukázková aplikace je nakonfigurovaná tak, aby podporovala cílení na více názvů souborů, aby všechny třídy obslužné rutiny byly umístěny v jedné složce:

Screenshot of the files in the Handlers folder of the project.

Třída VideoHandler obsahující mappery má název VideoHandler.cs. Implementace platformy jsou v souborech VideoHandler.Android.cs, VideoHandler.MaciOS.cs a VideoHandler.Windows.cs . Toto cílení na více názvů souborů je nakonfigurováno přidáním následujícího KÓDU XML do souboru projektu jako podřízených <Project> položek uzlu:

<!-- Android -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-android')) != true">
  <Compile Remove="**\*.Android.cs" />
  <None Include="**\*.Android.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- iOS and Mac Catalyst -->
<ItemGroup Condition="$(TargetFramework.StartsWith('net8.0-ios')) != true AND $(TargetFramework.StartsWith('net8.0-maccatalyst')) != true">
  <Compile Remove="**\*.MaciOS.cs" />
  <None Include="**\*.MaciOS.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

<!-- Windows -->
<ItemGroup Condition="$(TargetFramework.Contains('-windows')) != true ">
  <Compile Remove="**\*.Windows.cs" />
  <None Include="**\*.Windows.cs" Exclude="$(DefaultItemExcludes);$(DefaultExcludesInProjectFolder)" />
</ItemGroup>

Další informace o konfiguraci cílení na více verzí najdete v tématu Konfigurace cílení na více verzí.

Každá třída obslužné rutiny platformy by měla být částečnou třídou a odvozena od obecné ViewHandler třídy, která vyžaduje dva argumenty typu:

  • Třída pro řízení napříč platformami, která je odvozena od View.
  • Typ nativního zobrazení, který implementuje multiplatformní řízení na platformě. To by mělo být stejné jako typ PlatformView vlastnosti v obslužné rutině.

Důležité

Třída ViewHandler poskytuje VirtualView a PlatformView vlastnosti. Vlastnost VirtualView se používá pro přístup k ovládacímu prvku pro různé platformy z jeho obslužné rutiny. Tato PlatformView vlastnost se používá pro přístup k nativnímu zobrazení na každé platformě, která implementuje řízení napříč platformami.

Každá implementace obslužné rutiny platformy by měla přepsat následující metody:

  • CreatePlatformView, který by měl vytvořit a vrátit nativní zobrazení, které implementuje řízení napříč platformami.
  • ConnectHandler, který by měl provést jakékoli nastavení nativního zobrazení, jako je inicializace nativního zobrazení a provádění odběrů událostí.
  • DisconnectHandler, který by měl provést jakékoli nativní vyčištění zobrazení, jako je například zrušení odběru událostí a odstraňování objektů.

Důležité

Metoda DisconnectHandler není záměrně vyvolána rozhraním .NET MAUI. Místo toho ho musíte vyvolat sami z vhodného umístění v životním cyklu vaší aplikace. Další informace naleznete v tématu Nativní vyčištění zobrazení.

Každá obslužná rutina platformy by také měla implementovat akce definované ve slovníkech mapperu.

Kromě toho by každá obslužná rutina platformy měla také poskytnout kód, jak je potřeba, aby implementovaly funkce řízení napříč platformami na platformě. Alternativně to může poskytnout další typ, což je přístup, který zde přijímáme.

Android

Video se přehrával na Androidu VideoViewpomocí nástroje . Zde však byl zapouzdřen v MauiVideoPlayer typu, VideoView aby nativní zobrazení bylo odděleno od jeho obslužné rutiny. Následující příklad ukazuje částečnou VideoHandler třídu pro Android se třemi přepsáními:

#nullable enable
using Microsoft.Maui.Handlers;
using VideoDemos.Controls;
using VideoDemos.Platforms.Android;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        protected override MauiVideoPlayer CreatePlatformView() => new MauiVideoPlayer(Context, VirtualView);

        protected override void ConnectHandler(MauiVideoPlayer platformView)
        {
            base.ConnectHandler(platformView);

            // Perform any control setup here
        }

        protected override void DisconnectHandler(MauiVideoPlayer platformView)
        {
            platformView.Dispose();
            base.DisconnectHandler(platformView);
        }
        ...
    }
}

VideoHandler je odvozena od ViewHandler třídy, s obecným Video argumentem určujícím typ ovládacího prvku pro různé platformy a MauiVideoPlayer argument určující typ, který zapouzdřuje VideoView nativní zobrazení.

Přepsání CreatePlatformView vytvoří a vrátí MauiVideoPlayer objekt. Přepsání ConnectHandler je umístění pro provedení požadovaného nastavení nativního zobrazení. Přepsání DisconnectHandler je umístění pro provedení nativního čištění zobrazení, a proto volá metodu Dispose v MauiVideoPlayer instanci.

Obslužná rutina platformy musí také implementovat akce definované ve slovníku mapperu vlastností:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapAreTransportControlsEnabled(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateTransportControlsEnabled();
    }

    public static void MapSource(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateSource();
    }

    public static void MapIsLooping(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateIsLooping();
    }    

    public static void MapPosition(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdatePosition();
    }
    ...
}

Každá akce se provádí v reakci na vlastnost, která se mění v ovládacím prvku pro různé platformy, a je metoda, která vyžaduje obslužnou static rutinu a instance řízení napříč platformami jako argumenty. V každém případě akce volá metodu definovanou MauiVideoPlayer v typu.

Obslužná rutina platformy musí také implementovat akce definované ve slovníku mapperu příkazů:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
    {
        handler.PlatformView?.UpdateStatus();
    }

    public static void MapPlayRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PlayRequested(position);
    }

    public static void MapPauseRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PauseRequested(position);
    }

    public static void MapStopRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.StopRequested(position);
    }
    ...
}

Každá akce se provádí v reakci na příkaz odesílaný z víceplatformového ovládacího prvku a je metoda, která vyžaduje obslužnou static rutinu a instance řízení napříč platformami a volitelná data jako argumenty. V každém případě akce volá metodu definovanou ve MauiVideoPlayer třídě po extrahování volitelných dat.

V Androidu MauiVideoPlayer třída zapouzdřuje VideoView nativní zobrazení oddělené od jeho obslužné rutiny:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        MediaController _mediaController;
        bool _isPrepared;
        Context _context;
        Video _video;

        public MauiVideoPlayer(Context context, Video video) : base(context)
        {
            _context = context;
            _video = video;

            SetBackgroundColor(Color.Black);

            // Create a RelativeLayout for sizing the video
            RelativeLayout relativeLayout = new RelativeLayout(_context)
            {
                LayoutParameters = new CoordinatorLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent)
                {
                    Gravity = (int)GravityFlags.Center
                }
            };

            // Create a VideoView and position it in the RelativeLayout
            _videoView = new VideoView(context)
            {
                LayoutParameters = new RelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent)
            };

            // Add to the layouts
            relativeLayout.AddView(_videoView);
            AddView(relativeLayout);

            // Handle events
            _videoView.Prepared += OnVideoViewPrepared;
        }
        ...
    }
}

MauiVideoPlayer odvozuje od CoordinatorLayout, protože kořenové nativní zobrazení v aplikaci .NET MAUI v Androidu je CoordinatorLayout. MauiVideoPlayer I když by třída mohla odvozovat z jiných nativních typů Androidu, může být obtížné řídit umístění nativního zobrazení v některých scénářích.

Lze VideoView přidat přímo do objektu CoordinatorLayouta podle potřeby ho umístit do rozložení. Zde je však přidán CoordinatorLayoutAndroid RelativeLayout a VideoView je přidán do RelativeLayout. Parametry rozložení jsou nastaveny jak na RelativeLayoutVideoView střed stránky, tak i tak, aby VideoView byly na střed stránky, a rozbalí se tak, aby vyplnily dostupné místo při zachování poměru stran.

Konstruktor se také přihlásí k odběru VideoView.Prepared události. Tato událost se vyvolá, když je video připravené k přehrávání a v přepsání se odhlásí Dispose :

public class MauiVideoPlayer : CoordinatorLayout
{
    VideoView _videoView;
    Video _video;
    ...

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            _videoView.Prepared -= OnVideoViewPrepared;
            _videoView.Dispose();
            _videoView = null;
            _video = null;
        }

        base.Dispose(disposing);
    }
    ...
}

Kromě zrušení odběru PreparedDispose události provádí přepsání také nativní vyčištění zobrazení.

Poznámka:

Přepsání Dispose se volá přepsáním obslužné rutiny DisconnectHandler .

Ovládací prvky přenosu platformy zahrnují tlačítka, která přehrávají, pozastavují a zastavují video a poskytují je typ Androidu MediaController . Video.AreTransportControlsEnabled Pokud je vlastnost nastavena truena , MediaController je nastavena jako přehrávač médií objektu VideoView. K tomu dochází, protože když AreTransportControlsEnabled je vlastnost nastavena, mapovač vlastností obslužné rutiny zajišťuje, že MapAreTransportControlsEnabled metoda je vyvolána, což následně volá metodu UpdateTransportControlsEnabled v MauiVideoPlayer:

public class MauiVideoPlayer : CoordinatorLayout
{
    VideoView _videoView;
    MediaController _mediaController;
    Video _video;
    ...

    public void UpdateTransportControlsEnabled()
    {
        if (_video.AreTransportControlsEnabled)
        {
            _mediaController = new MediaController(_context);
            _mediaController.SetMediaPlayer(_videoView);
            _videoView.SetMediaController(_mediaController);
        }
        else
        {
            _videoView.SetMediaController(null);
            if (_mediaController != null)
            {
                _mediaController.SetMediaPlayer(null);
                _mediaController = null;
            }
        }
    }
    ...
}

Ovládací prvky přenosu zmizí, pokud se nepoužívají, ale dají se obnovit klepnutím na video.

Pokud je vlastnost nastavena Video.AreTransportControlsEnabledfalsena , MediaController je odebrána jako přehrávač médií objektu VideoView. V tomto scénáři pak můžete ovládat přehrávání videa programově nebo zadat vlastní ovládací prvky přenosu. Další informace naleznete v tématu Vytvoření vlastních ovládacích prvků přenosu.

iOS a Mac Catalyst

Video se přehrával v systémech iOS a Mac Catalyst s a AVPlayer .AVPlayerViewController Zde jsou však tyto typy zapouzdřeny v MauiVideoPlayer typu, aby nativní zobrazení byla oddělena od jejich obslužné rutiny. Následující příklad ukazuje částečnou VideoHandler třídu pro iOS se třemi přepsáním:

using Microsoft.Maui.Handlers;
using VideoDemos.Controls;
using VideoDemos.Platforms.MaciOS;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        protected override MauiVideoPlayer CreatePlatformView() => new MauiVideoPlayer(VirtualView);

        protected override void ConnectHandler(MauiVideoPlayer platformView)
        {
            base.ConnectHandler(platformView);

            // Perform any control setup here
        }

        protected override void DisconnectHandler(MauiVideoPlayer platformView)
        {
            platformView.Dispose();
            base.DisconnectHandler(platformView);
        }
        ...
    }
}

VideoHandler je odvozena od ViewHandler třídy, s obecným Video argumentem určujícím typ ovládacího prvku pro různé platformy a MauiVideoPlayer argument určující typ, který zapouzdřuje AVPlayer a AVPlayerViewController nativní zobrazení.

Přepsání CreatePlatformView vytvoří a vrátí MauiVideoPlayer objekt. Přepsání ConnectHandler je umístění pro provedení požadovaného nastavení nativního zobrazení. Přepsání DisconnectHandler je umístění pro provedení nativního čištění zobrazení, a proto volá metodu Dispose v MauiVideoPlayer instanci.

Obslužná rutina platformy musí také implementovat akce definované ve slovníku mapperu vlastností:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapAreTransportControlsEnabled(VideoHandler handler, Video video)
    {
        handler?.PlatformView.UpdateTransportControlsEnabled();
    }

    public static void MapSource(VideoHandler handler, Video video)
    {
        handler?.PlatformView.UpdateSource();
    }

    public static void MapIsLooping(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateIsLooping();
    }    

    public static void MapPosition(VideoHandler handler, Video video)
    {
        handler?.PlatformView.UpdatePosition();
    }
    ...
}

Každá akce se provádí v reakci na vlastnost, která se mění v ovládacím prvku pro různé platformy, a je metoda, která vyžaduje obslužnou static rutinu a instance řízení napříč platformami jako argumenty. V každém případě akce volá metodu definovanou MauiVideoPlayer v typu.

Obslužná rutina platformy musí také implementovat akce definované ve slovníku mapperu příkazů:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
    {
        handler.PlatformView?.UpdateStatus();
    }

    public static void MapPlayRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PlayRequested(position);
    }

    public static void MapPauseRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PauseRequested(position);
    }

    public static void MapStopRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.StopRequested(position);
    }
    ...
}

Každá akce se provádí v reakci na příkaz odesílaný z víceplatformového ovládacího prvku a je metoda, která vyžaduje obslužnou static rutinu a instance řízení napříč platformami a volitelná data jako argumenty. V každém případě akce volá metodu definovanou ve MauiVideoPlayer třídě po extrahování volitelných dat.

V systémech iOS a Mac Catalyst MauiVideoPlayer třída zapouzdřuje AVPlayer a AVPlayerViewController typy, aby byla nativní zobrazení oddělená od jejich obslužné rutiny:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerViewController _playerViewController;
        Video _video;
        ...

        public MauiVideoPlayer(Video video)
        {
            _video = video;

            _playerViewController = new AVPlayerViewController();
            _player = new AVPlayer();
            _playerViewController.Player = _player;
            _playerViewController.View.Frame = this.Bounds;

#if IOS16_0_OR_GREATER || MACCATALYST16_1_OR_GREATER
            // On iOS 16 and Mac Catalyst 16, for Shell-based apps, the AVPlayerViewController has to be added to the parent ViewController, otherwise the transport controls won't be displayed.
            var viewController = WindowStateManager.Default.GetCurrentUIViewController();

            // If there's no view controller, assume it's not Shell and continue because the transport controls will still be displayed.
            if (viewController?.View is not null)
            {
                // Zero out the safe area insets of the AVPlayerViewController
                UIEdgeInsets insets = viewController.View.SafeAreaInsets;
                _playerViewController.AdditionalSafeAreaInsets = new UIEdgeInsets(insets.Top * -1, insets.Left, insets.Bottom * -1, insets.Right);

                // Add the View from the AVPlayerViewController to the parent ViewController
                viewController.View.AddSubview(_playerViewController.View);
            }
#endif
            // Use the View from the AVPlayerViewController as the native control
            AddSubview(_playerViewController.View);
        }
        ...
    }
}

MauiVideoPlayer je odvozen od UIView, což je základní třída v iOS a Mac Catalyst pro objekty, které zobrazují obsah a zpracovávají uživatelské interakce s tímto obsahem. Konstruktor vytvoří AVPlayer objekt, který spravuje přehrávání a časování multimediálního souboru a nastaví ho jako Player hodnotu AVPlayerViewControllervlastnosti . Zobrazí AVPlayerViewController obsah z AVPlayer obsahu a zobrazí ovládací prvky přenosu a další funkce. Velikost a umístění ovládacího prvku se pak nastaví, což zajistí, že video bude na střed na stránce a rozbalí se tak, aby vyplnilo dostupné místo při zachování poměru stran. V systémech iOS 16 a Mac Catalyst 16 AVPlayerViewController se musí přidat do nadřazeného objektu ViewController pro aplikace založené na prostředí, jinak se nezobrazují ovládací prvky přenosu. Nativní zobrazení, což je zobrazení z objektu AVPlayerViewController, se pak přidá na stránku.

Metoda Dispose zodpovídá za provádění nativního čištění zobrazení:

public class MauiVideoPlayer : UIView
{
    AVPlayer _player;
    AVPlayerViewController _playerViewController;
    Video _video;
    ...

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_player != null)
            {
                DestroyPlayedToEndObserver();
                _player.ReplaceCurrentItemWithPlayerItem(null);
                _player.Dispose();
            }
            if (_playerViewController != null)
                _playerViewController.Dispose();

            _video = null;
        }

        base.Dispose(disposing);
    }
    ...
}

V některých scénářích se videa přehrávají i po přechodu na stránku přehrávání videa. Pokud chcete video zastavit, ReplaceCurrentItemWithPlayerItem nastaví se null v Dispose přepsání a provede se další nativní vyčištění zobrazení.

Poznámka:

Přepsání Dispose se volá přepsáním obslužné rutiny DisconnectHandler .

Ovládací prvky přenosu platformy zahrnují tlačítka, která přehrávají, pozastavují a zastavují video a poskytují je typ AVPlayerViewController . Pokud je vlastnost nastavena Video.AreTransportControlsEnabled na true, AVPlayerViewController zobrazí se jeho ovládací prvky přehrávání. K tomu dochází, protože když AreTransportControlsEnabled je vlastnost nastavena, mapovač vlastností obslužné rutiny zajišťuje, že MapAreTransportControlsEnabled metoda je vyvolána, což následně volá metodu UpdateTransportControlsEnabled v MauiVideoPlayer:

public class MauiVideoPlayer : UIView
{
    AVPlayerViewController _playerViewController;
    Video _video;
    ...

    public void UpdateTransportControlsEnabled()
    {
        _playerViewController.ShowsPlaybackControls = _video.AreTransportControlsEnabled;
    }
    ...
}

Ovládací prvky přenosu zmizí, pokud se nepoužívají, ale dají se obnovit klepnutím na video.

Video.AreTransportControlsEnabled Pokud je vlastnost nastavena na false, AVPlayerViewController nezobrazuje ovládací prvky přehrávání. V tomto scénáři pak můžete ovládat přehrávání videa programově nebo zadat vlastní ovládací prvky přenosu. Další informace naleznete v tématu Vytvoření vlastních ovládacích prvků přenosu.

Okna

Video se přehraje ve Windows pomocí nástroje MediaPlayerElement. Zde však byl zapouzdřen v MauiVideoPlayer typu, MediaPlayerElement aby nativní zobrazení bylo odděleno od jeho obslužné rutiny. Následující příklad ukazuje částečnou VideoHandler třídu fo Windows se třemi přepsáními:

#nullable enable
using Microsoft.Maui.Handlers;
using VideoDemos.Controls;
using VideoDemos.Platforms.Windows;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        protected override MauiVideoPlayer CreatePlatformView() => new MauiVideoPlayer(VirtualView);

        protected override void ConnectHandler(MauiVideoPlayer platformView)
        {
            base.ConnectHandler(platformView);

            // Perform any control setup here
        }

        protected override void DisconnectHandler(MauiVideoPlayer platformView)
        {
            platformView.Dispose();
            base.DisconnectHandler(platformView);
        }
        ...
    }
}

VideoHandler je odvozena od ViewHandler třídy, s obecným Video argumentem určujícím typ ovládacího prvku pro různé platformy a MauiVideoPlayer argument určující typ, který zapouzdřuje MediaPlayerElement nativní zobrazení.

Přepsání CreatePlatformView vytvoří a vrátí MauiVideoPlayer objekt. Přepsání ConnectHandler je umístění pro provedení požadovaného nastavení nativního zobrazení. Přepsání DisconnectHandler je umístění pro provedení nativního čištění zobrazení, a proto volá metodu Dispose v MauiVideoPlayer instanci.

Obslužná rutina platformy musí také implementovat akce definované ve slovníku mapperu vlastností:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapAreTransportControlsEnabled(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateTransportControlsEnabled();
    }

    public static void MapSource(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateSource();
    }

    public static void MapIsLooping(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdateIsLooping();
    }

    public static void MapPosition(VideoHandler handler, Video video)
    {
        handler.PlatformView?.UpdatePosition();
    }
    ...
}

Každá akce se provádí v reakci na vlastnost, která se mění v ovládacím prvku pro různé platformy, a je metoda, která vyžaduje obslužnou static rutinu a instance řízení napříč platformami jako argumenty. V každém případě akce volá metodu definovanou MauiVideoPlayer v typu.

Obslužná rutina platformy musí také implementovat akce definované ve slovníku mapperu příkazů:

public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
{
    ...
    public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
    {
        handler.PlatformView?.UpdateStatus();
    }

    public static void MapPlayRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PlayRequested(position);
    }

    public static void MapPauseRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.PauseRequested(position);
    }

    public static void MapStopRequested(VideoHandler handler, Video video, object? args)
    {
        if (args is not VideoPositionEventArgs)
            return;

        TimeSpan position = ((VideoPositionEventArgs)args).Position;
        handler.PlatformView?.StopRequested(position);
    }
    ...
}

Každá akce se provádí v reakci na příkaz odesílaný z víceplatformového ovládacího prvku a je metoda, která vyžaduje obslužnou static rutinu a instance řízení napříč platformami a volitelná data jako argumenty. V každém případě akce volá metodu definovanou ve MauiVideoPlayer třídě po extrahování volitelných dat.

Ve MauiVideoPlayer Windows třída zapouzdřuje MediaPlayerElement nativní zobrazení oddělené od jeho obslužné rutiny:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        ...

        public MauiVideoPlayer(Video video)
        {
            _video = video;
            _mediaPlayerElement = new MediaPlayerElement();
            this.Children.Add(_mediaPlayerElement);
        }
        ...
    }
}

MauiVideoPlayer je odvozen od Grid, a je MediaPlayerElement přidána jako dítě Grid. To umožňuje MediaPlayerElement automaticky vyplnit všechny dostupné místo.

Metoda Dispose zodpovídá za provádění nativního čištění zobrazení:

public class MauiVideoPlayer : Grid, IDisposable
{
    MediaPlayerElement _mediaPlayerElement;
    Video _video;
    bool _isMediaPlayerAttached;
    ...

    public void Dispose()
    {
        if (_isMediaPlayerAttached)
        {
            _mediaPlayerElement.MediaPlayer.MediaOpened -= OnMediaPlayerMediaOpened;
            _mediaPlayerElement.MediaPlayer.Dispose();
        }
        _mediaPlayerElement = null;
    }
    ...
}

Kromě zrušení odběru MediaOpenedDispose události provádí přepsání také nativní vyčištění zobrazení.

Poznámka:

Přepsání Dispose se volá přepsáním obslužné rutiny DisconnectHandler .

Ovládací prvky přenosu platformy zahrnují tlačítka, která přehrávají, pozastavují a zastavují video a poskytují je typ MediaPlayerElement . Pokud je vlastnost nastavena Video.AreTransportControlsEnabled na true, MediaPlayerElement zobrazí se jeho ovládací prvky přehrávání. K tomu dochází, protože když AreTransportControlsEnabled je vlastnost nastavena, mapovač vlastností obslužné rutiny zajišťuje, že MapAreTransportControlsEnabled metoda je vyvolána, což následně volá metodu UpdateTransportControlsEnabled v MauiVideoPlayer:

public class MauiVideoPlayer : Grid, IDisposable
{
    MediaPlayerElement _mediaPlayerElement;
    Video _video;
    bool _isMediaPlayerAttached;
    ...

    public void UpdateTransportControlsEnabled()
    {
        _mediaPlayerElement.AreTransportControlsEnabled = _video.AreTransportControlsEnabled;
    }
    ...

}

Video.AreTransportControlsEnabled Pokud je vlastnost nastavena na false, MediaPlayerElement nezobrazuje ovládací prvky přehrávání. V tomto scénáři pak můžete ovládat přehrávání videa programově nebo zadat vlastní ovládací prvky přenosu. Další informace naleznete v tématu Vytvoření vlastních ovládacích prvků přenosu.

Převod ovládacího prvku pro různé platformy na ovládací prvek platformy

Jakýkoliv multiplatformní ovládací prvek .NET MAUI, který je odvozen od Element, lze převést na jeho základní ovládací prvek platformy pomocí ToPlatform metody rozšíření:

  • V Androidu ToPlatform převede ovládací prvek .NET MAUI na objekt Androidu View .
  • V systémech iOS a Mac Catalyst ToPlatform převede ovládací prvek .NET MAUI na UIView objekt.
  • Ve Windows ToPlatform převede ovládací prvek .NET MAUI na FrameworkElement objekt.

Poznámka:

Metoda ToPlatform je v Microsoft.Maui.Platform oboru názvů.

Na všech platformách metoda ToPlatform vyžaduje MauiContext argument.

Metoda ToPlatform může převést ovládací prvek mezi platformami na základní ovládací prvek platformy z kódu platformy, například v částečné třídě obslužné rutiny pro platformu:

using Microsoft.Maui.Handlers;
using Microsoft.Maui.Platform;
using VideoDemos.Controls;
using VideoDemos.Platforms.Android;

namespace VideoDemos.Handlers
{
    public partial class VideoHandler : ViewHandler<Video, MauiVideoPlayer>
    {
        ...
        public static void MapSource(VideoHandler handler, Video video)
        {
            handler.PlatformView?.UpdateSource();

            // Convert cross-platform control to its underlying platform control
            MauiVideoPlayer mvp = (MauiVideoPlayer)video.ToPlatform(handler.MauiContext);
            ...
        }
        ...
    }
}

V tomto příkladu VideoHandler metoda v částečné třídě pro Android MapSource převede Video instanci na MauiVideoPlayer objekt.

Metoda ToPlatform může také převést ovládací prvek mezi platformami na základní ovládací prvek platformy z kódu pro různé platformy:

using Microsoft.Maui.Platform;

namespace VideoDemos.Views;

public partial class MyPage : ContentPage
{
    ...
    protected override void OnHandlerChanged()
    {
        // Convert cross-platform control to its underlying platform control
#if ANDROID
        Android.Views.View nativeView = video.ToPlatform(video.Handler.MauiContext);
#elif IOS || MACCATALYST
        UIKit.UIView nativeView = video.ToPlatform(video.Handler.MauiContext);
#elif WINDOWS
        Microsoft.UI.Xaml.FrameworkElement nativeView = video.ToPlatform(video.Handler.MauiContext);
#endif
        ...
    }
    ...
}

V tomto příkladu je pojmenovaný video ovládací prvek pro různé platformy Video převeden na jeho základní nativní zobrazení na každé platformě v přepsáníOnHandlerChanged(). Toto přepsání se volá, když je k dispozici a inicializován nativní zobrazení, které implementuje řízení napříč platformami. Objekt vrácený metodou ToPlatform lze přetypovat na jeho přesný nativní typ, který zde je MauiVideoPlayer.

Přehrání videa

Třída Video definuje Source vlastnost, která se používá k určení zdroje videosouboru a AutoPlay vlastnosti. AutoPlay výchozí hodnota trueje , což znamená, že video by se mělo začít přehrávat automaticky po Source nastavení. Definice těchto vlastností naleznete v tématu Vytvoření multiplatformního ovládacího prvku.

Vlastnost Source je typu VideoSource, což je abstraktní třída, která se skládá ze tří statických metod, které vytvoří instanci tří tříd odvozených z VideoSource:

using System.ComponentModel;

namespace VideoDemos.Controls
{
    [TypeConverter(typeof(VideoSourceConverter))]
    public abstract class VideoSource : Element
    {
        public static VideoSource FromUri(string uri)
        {
            return new UriVideoSource { Uri = uri };
        }

        public static VideoSource FromFile(string file)
        {
            return new FileVideoSource { File = file };
        }

        public static VideoSource FromResource(string path)
        {
            return new ResourceVideoSource { Path = path };
        }
    }
}

Třída VideoSource obsahuje TypeConverter atribut, který odkazuje na VideoSourceConverter:

using System.ComponentModel;

namespace VideoDemos.Controls
{
    public class VideoSourceConverter : TypeConverter, IExtendedTypeConverter
    {
        object IExtendedTypeConverter.ConvertFromInvariantString(string value, IServiceProvider serviceProvider)
        {
            if (!string.IsNullOrWhiteSpace(value))
            {
                Uri uri;
                return Uri.TryCreate(value, UriKind.Absolute, out uri) && uri.Scheme != "file" ?
                    VideoSource.FromUri(value) : VideoSource.FromResource(value);
            }
            throw new InvalidOperationException("Cannot convert null or whitespace to VideoSource.");
        }
    }
}

Převaděč typů se vyvolá, když Source je vlastnost nastavena na řetězec v XAML. Metoda ConvertFromInvariantString se pokusí převést řetězec na Uri objekt. Pokud je to úspěšné a schéma není file, pak metoda vrátí UriVideoSource. V opačném případě vrátí hodnotu ResourceVideoSource.

Přehrávání webového videa

Třída UriVideoSource se používá k určení vzdáleného videa pomocí identifikátoru URI. Uri Definuje vlastnost typustring:

namespace VideoDemos.Controls
{
    public class UriVideoSource : VideoSource
    {
        public static readonly BindableProperty UriProperty =
            BindableProperty.Create(nameof(Uri), typeof(string), typeof(UriVideoSource));

        public string Uri
        {
            get { return (string)GetValue(UriProperty); }
            set { SetValue(UriProperty, value); }
        }
    }
}

Pokud je vlastnost nastavena Source na UriVideoSource, mapovač vlastností obslužné rutiny zajišťuje, že MapSource metoda je vyvolána:

public static void MapSource(VideoHandler handler, Video video)
{
    handler?.PlatformView.UpdateSource();
}

Metoda MapSource pak volá metodu UpdateSource ve vlastnosti obslužné rutiny PlatformView . Vlastnost PlatformView , která je typu MauiVideoPlayer, představuje nativní zobrazení, které poskytuje implementaci přehrávače videa na každé platformě.

Android

Video se přehrával na Androidu VideoViewpomocí nástroje . Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu UriVideoSource:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Video _video;
        ...

        public void UpdateSource()
        {
            _isPrepared = false;
            bool hasSetSource = false;

            if (_video.Source is UriVideoSource)
            {
                string uri = (_video.Source as UriVideoSource).Uri;
                if (!string.IsNullOrWhiteSpace(uri))
                {
                    _videoView.SetVideoURI(Uri.Parse(uri));
                    hasSetSource = true;
                }
            }
            ...

            if (hasSetSource && _video.AutoPlay)
            {
                _videoView.Start();
            }
        }
        ...
    }
}

Při zpracování objektů typu UriVideoSource, SetVideoUri metoda VideoView se používá k určení videa, které se má přehrát, s objektem Android Uri vytvořeným z řetězcového identifikátoru URI.

Vlastnost AutoPlay nemá žádný ekvivalent pro VideoView, takže Start metoda je volána, pokud bylo nastaveno nové video.

iOS a Mac Catalyst

Chcete-li přehrát video na iOS a Mac Catalyst, objekt typu AVAsset je vytvořen pro zapouzdření videa a který se používá k vytvoření AVPlayerItem, který se pak předá objektu AVPlayer . Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu UriVideoSource:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerItem _playerItem;
        Video _video;
        ...

        public void UpdateSource()
        {
            AVAsset asset = null;

            if (_video.Source is UriVideoSource)
            {
                string uri = (_video.Source as UriVideoSource).Uri;
                if (!string.IsNullOrWhiteSpace(uri))
                    asset = AVAsset.FromUrl(new NSUrl(uri));
            }
            ...

            if (asset != null)
                _playerItem = new AVPlayerItem(asset);
            else
                _playerItem = null;

            _player.ReplaceCurrentItemWithPlayerItem(_playerItem);
            if (_playerItem != null && _video.AutoPlay)
            {
                _player.Play();
            }
        }
        ...
    }
}

Při zpracování objektů typu UriVideoSourcese statická AVAsset.FromUrl metoda používá k určení videa, které se má přehrát, s objektem iOS NSUrl vytvořeným z identifikátoru URI řetězce.

Vlastnost AutoPlay nemá v třídách videa pro iOS žádný ekvivalent, takže vlastnost je zkoumána na konci UpdateSource metody volání Play metody na objektu AVPlayer .

V některých případech v iOSu se videa po přechodu na stránku přehrávání videa přehrávají dál. Pokud chcete video zastavit, ReplaceCurrentItemWithPlayerItem nastaví null se v přepsání Dispose :

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_player != null)
        {
            _player.ReplaceCurrentItemWithPlayerItem(null);
            ...
        }
        ...
    }
    base.Dispose(disposing);
}

Okna

Video se přehrával ve Windows pomocí MediaPlayerElement. Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu UriVideoSource:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        public async void UpdateSource()
        {
            bool hasSetSource = false;

            if (_video.Source is UriVideoSource)
            {
                string uri = (_video.Source as UriVideoSource).Uri;
                if (!string.IsNullOrWhiteSpace(uri))
                {
                    _mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri(uri));
                    hasSetSource = true;
                }
            }
            ...

            if (hasSetSource && !_isMediaPlayerAttached)
            {
                _isMediaPlayerAttached = true;
                _mediaPlayerElement.MediaPlayer.MediaOpened += OnMediaPlayerMediaOpened;
            }

            if (hasSetSource && _video.AutoPlay)
            {
                _mediaPlayerElement.AutoPlay = true;
            }
        }
        ...
    }
}

Při zpracování objektů typu UriVideoSourceje MediaPlayerElement.Source vlastnost nastavena na MediaSource objekt, který inicializuje s identifikátorem Uri URI videa, který se má přehrát. MediaPlayerElement.Source Po nastavení OnMediaPlayerMediaOpened je metoda obslužné rutiny události registrována proti MediaPlayerElement.MediaPlayer.MediaOpened události. Tato obslužná rutina události slouží k nastavení Duration vlastnosti Video ovládacího prvku.

Na konci UpdateSource metody je Video.AutoPlay vlastnost zkoumána a pokud je true MediaPlayerElement.AutoPlay , vlastnost je nastavena tak, aby true se spustilo přehrávání videa.

Přehrání prostředku videa

Třída ResourceVideoSource se používá pro přístup k videosouborům, které jsou vložené v aplikaci. Path Definuje vlastnost typustring:

namespace VideoDemos.Controls
{
    public class ResourceVideoSource : VideoSource
    {
        public static readonly BindableProperty PathProperty =
            BindableProperty.Create(nameof(Path), typeof(string), typeof(ResourceVideoSource));

        public string Path
        {
            get { return (string)GetValue(PathProperty); }
            set { SetValue(PathProperty, value); }
        }
    }
}

Pokud je vlastnost nastavena Source na ResourceVideoSource, mapovač vlastností obslužné rutiny zajišťuje, že MapSource metoda je vyvolána:

public static void MapSource(VideoHandler handler, Video video)
{
    handler?.PlatformView.UpdateSource();
}

Metoda MapSource pak volá metodu UpdateSource ve vlastnosti obslužné rutiny PlatformView . Vlastnost PlatformView , která je typu MauiVideoPlayer, představuje nativní zobrazení, které poskytuje implementaci přehrávače videa na každé platformě.

Android

Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu ResourceVideoSource:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Context _context;
        Video _video;
        ...

        public void UpdateSource()
        {
            _isPrepared = false;
            bool hasSetSource = false;
            ...

            else if (_video.Source is ResourceVideoSource)
            {
                string package = Context.PackageName;
                string path = (_video.Source as ResourceVideoSource).Path;
                if (!string.IsNullOrWhiteSpace(path))
                {
                    string assetFilePath = "content://" + package + "/" + path;
                    _videoView.SetVideoPath(assetFilePath);
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Při zpracování objektů typu ResourceVideoSourcese SetVideoPath metoda VideoView slouží k určení videa, které se má přehrát, s řetězcovým argumentem, který kombinuje název balíčku aplikace s názvem videa.

Soubor videa o prostředku je uložený ve složce prostředků balíčku a vyžaduje, aby k němu přistupoval poskytovatel obsahu. Poskytovatel obsahu je poskytován VideoProvider třídou, která vytvoří AssetFileDescriptor objekt, který poskytuje přístup k videosouboru:

using Android.Content;
using Android.Content.Res;
using Android.Database;
using Debug = System.Diagnostics.Debug;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    [ContentProvider(new string[] { "com.companyname.videodemos" })]
    public class VideoProvider : ContentProvider
    {
        public override AssetFileDescriptor OpenAssetFile(Uri uri, string mode)
        {
            var assets = Context.Assets;
            string fileName = uri.LastPathSegment;
            if (fileName == null)
                throw new FileNotFoundException();

            AssetFileDescriptor afd = null;
            try
            {
                afd = assets.OpenFd(fileName);
            }
            catch (IOException ex)
            {
                Debug.WriteLine(ex);
            }
            return afd;
        }

        public override bool OnCreate()
        {
            return false;
        }
        ...
    }
}

iOS a Mac Catalyst

Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu ResourceVideoSource:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        Video _video;
        ...

        public void UpdateSource()
        {
            AVAsset asset = null;
            ...

            else if (_video.Source is ResourceVideoSource)
            {
                string path = (_video.Source as ResourceVideoSource).Path;
                if (!string.IsNullOrWhiteSpace(path))
                {
                    string directory = Path.GetDirectoryName(path);
                    string filename = Path.GetFileNameWithoutExtension(path);
                    string extension = Path.GetExtension(path).Substring(1);
                    NSUrl url = NSBundle.MainBundle.GetUrlForResource(filename, extension, directory);
                    asset = AVAsset.FromUrl(url);
                }
            }
            ...
        }
        ...
    }
}

Při zpracování objektů typu ResourceVideoSourcese GetUrlForResource metoda NSBundle používá k načtení souboru z balíčku aplikace. Úplná cesta musí být rozdělena do názvu souboru, přípony a adresáře.

V některých případech v iOSu se videa po přechodu na stránku přehrávání videa přehrávají dál. Pokud chcete video zastavit, ReplaceCurrentItemWithPlayerItem nastaví null se v přepsání Dispose :

protected override void Dispose(bool disposing)
{
    if (disposing)
    {
        if (_player != null)
        {
            _player.ReplaceCurrentItemWithPlayerItem(null);
            ...
        }
        ...
    }
    base.Dispose(disposing);
}

Okna

Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu ResourceVideoSource:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        ...

        public async void UpdateSource()
        {
            bool hasSetSource = false;

            ...
            else if (_video.Source is ResourceVideoSource)
            {
                string path = "ms-appx:///" + (_video.Source as ResourceVideoSource).Path;
                if (!string.IsNullOrWhiteSpace(path))
                {
                    _mediaPlayerElement.Source = MediaSource.CreateFromUri(new Uri(path));
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Při zpracování objektů typu ResourceVideoSourceje MediaPlayerElement.Source vlastnost nastavena na MediaSource objekt, který inicializuje Uri cestu k prostředku videa s předponou ms-appx:///.

Přehrání videosouboru z knihovny zařízení

Třída FileVideoSource slouží k přístupu k videím v knihovně videí zařízení. File Definuje vlastnost typustring:

namespace VideoDemos.Controls
{
    public class FileVideoSource : VideoSource
    {
        public static readonly BindableProperty FileProperty =
            BindableProperty.Create(nameof(File), typeof(string), typeof(FileVideoSource));

        public string File
        {
            get { return (string)GetValue(FileProperty); }
            set { SetValue(FileProperty, value); }
        }
    }
}

Pokud je vlastnost nastavena Source na FileVideoSource, mapovač vlastností obslužné rutiny zajišťuje, že MapSource metoda je vyvolána:

public static void MapSource(VideoHandler handler, Video video)
{
    handler?.PlatformView.UpdateSource();
}

Metoda MapSource pak volá metodu UpdateSource ve vlastnosti obslužné rutiny PlatformView . Vlastnost PlatformView , která je typu MauiVideoPlayer, představuje nativní zobrazení, které poskytuje implementaci přehrávače videa na každé platformě.

Android

Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu FileVideoSource:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Video _video;
        ...

        public void UpdateSource()
        {
            _isPrepared = false;
            bool hasSetSource = false;
            ...

            else if (_video.Source is FileVideoSource)
            {
                string filename = (_video.Source as FileVideoSource).File;
                if (!string.IsNullOrWhiteSpace(filename))
                {
                    _videoView.SetVideoPath(filename);
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Při zpracování objektů typu FileVideoSourcese SetVideoPath metoda VideoView slouží k určení videosouboru, který se má přehrát.

iOS a Mac Catalyst

Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu FileVideoSource:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        Video _video;
        ...

        public void UpdateSource()
        {
            AVAsset asset = null;
            ...

            else if (_video.Source is FileVideoSource)
            {
                string uri = (_video.Source as FileVideoSource).File;
                if (!string.IsNullOrWhiteSpace(uri))
                    asset = AVAsset.FromUrl(NSUrl.CreateFileUrl(new [] { uri }));
            }
            ...
        }
        ...
    }
}

Při zpracování objektů typu FileVideoSourcese statická AVAsset.FromUrl metoda používá k určení videosouboru, který se má přehrát, s NSUrl.CreateFileUrl metodou vytvoření objektu iOS NSUrl z řetězcového identifikátoru URI.

Okna

Následující příklad kódu ukazuje, jak UpdateSource metoda zpracovává Source vlastnost, když je typu FileVideoSource:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        ...

        public async void UpdateSource()
        {
            bool hasSetSource = false;

            ...
            else if (_video.Source is FileVideoSource)
            {
                string filename = (_video.Source as FileVideoSource).File;
                if (!string.IsNullOrWhiteSpace(filename))
                {
                    StorageFile storageFile = await StorageFile.GetFileFromPathAsync(filename);
                    _mediaPlayerElement.Source = MediaSource.CreateFromStorageFile(storageFile);
                    hasSetSource = true;
                }
            }
            ...
        }
        ...
    }
}

Při zpracování objektů typu FileVideoSourcese název souboru videa převede na StorageFile objekt. Pak metoda vrátí MediaSource objekt, MediaSource.CreateFromStorageFile který je nastaven jako hodnota MediaPlayerElement.Source vlastnosti.

Smyčka videa

Třída Video definuje IsLooping vlastnost, která umožňuje ovládacímu prvku automaticky nastavit pozici videa na začátek po dosažení jeho konce. Výchozí hodnota je false, což značí, že videa se automaticky nesmyčují.

IsLooping Když je vlastnost nastavena, mapovač vlastností obslužné rutiny zajišťuje, že MapIsLooping metoda je vyvolána:

public static void MapIsLooping(VideoHandler handler, Video video)
{
    handler.PlatformView?.UpdateIsLooping();
}  

Metoda MapIsLooping zase volá metodu UpdateIsLooping ve vlastnosti obslužné rutiny PlatformView . Vlastnost PlatformView , která je typu MauiVideoPlayer, představuje nativní zobrazení, které poskytuje implementaci přehrávače videa na každé platformě.

Android

Následující příklad kódu ukazuje, jak UpdateIsLooping metoda v Androidu umožňuje smyčku videa:

using Android.Content;
using Android.Media;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout, MediaPlayer.IOnPreparedListener
    {
        VideoView _videoView;
        Video _video;
        ...

        public void UpdateIsLooping()
        {
            if (_video.IsLooping)
            {
                _videoView.SetOnPreparedListener(this);
            }
            else
            {
                _videoView.SetOnPreparedListener(null);
            }
        }

        public void OnPrepared(MediaPlayer mp)
        {
            mp.Looping = _video.IsLooping;
        }
        ...
    }
}

Pokud chcete povolit smyčku videa, MauiVideoPlayer třída implementuje MediaPlayer.IOnPreparedListener rozhraní. Toto rozhraní definuje OnPrepared zpětné volání, které se vyvolá, když je zdroj médií připravený k přehrávání. Video.IsLooping Pokud je truevlastnost , UpdateIsLooping metoda nastaví MauiVideoPlayer jako objekt, který poskytuje OnPrepared zpětné volání. Zpětné volání nastaví MediaPlayer.IsLooping vlastnost na hodnotu Video.IsLooping vlastnosti.

iOS a Mac Catalyst

Následující příklad kódu ukazuje, jak UpdateIsLooping metoda v iOS a Mac Catalyst umožňuje smyčky videa:

using System.Diagnostics;
using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerViewController _playerViewController;
        Video _video;
        NSObject? _playedToEndObserver;
        ...

        public void UpdateIsLooping()
        {
            DestroyPlayedToEndObserver();
            if (_video.IsLooping)
            {
                _player.ActionAtItemEnd = AVPlayerActionAtItemEnd.None;
                _playedToEndObserver = NSNotificationCenter.DefaultCenter.AddObserver(AVPlayerItem.DidPlayToEndTimeNotification, PlayedToEnd);
            }
            else
                _player.ActionAtItemEnd = AVPlayerActionAtItemEnd.Pause;
        }

        void PlayedToEnd(NSNotification notification)
        {
            if (_video == null || notification.Object != _playerViewController.Player?.CurrentItem)
                return;

            _playerViewController.Player?.Seek(CMTime.Zero);
        }
        ...
    }
}

V iOSu a Mac Catalystu se k provedení zpětného volání použije oznámení, když se video přehraje na konec. Video.IsLooping Pokud je truevlastnost , UpdateIsLooping metoda přidá pozorovatel pro AVPlayerItem.DidPlayToEndTimeNotification oznámení a spustí metodu PlayedToEnd při přijetí oznámení. Tato metoda zase obnoví přehrávání od začátku videa. Video.IsLooping Pokud je falsetato vlastnost, video se pozastaví na konci přehrávání.

Protože MauiVideoPlayer přidá pozorovatele pro oznámení, musí také odebrat pozorovatele při provádění nativního čištění zobrazení. To se provádí v přepsání Dispose :

public class MauiVideoPlayer : UIView
{
    AVPlayer _player;
    AVPlayerViewController _playerViewController;
    Video _video;
    NSObject? _playedToEndObserver;
    ...

    protected override void Dispose(bool disposing)
    {
        if (disposing)
        {
            if (_player != null)
            {
                DestroyPlayedToEndObserver();
                ...
            }
            ...
        }

        base.Dispose(disposing);
    }

    void DestroyPlayedToEndObserver()
    {
        if (_playedToEndObserver != null)
        {
            NSNotificationCenter.DefaultCenter.RemoveObserver(_playedToEndObserver);
            DisposeObserver(ref _playedToEndObserver);
        }
    }

    void DisposeObserver(ref NSObject? disposable)
    {
        disposable?.Dispose();
        disposable = null;
    }
    ...
}

Přepsání Dispose volá metodu DestroyPlayedToEndObserver , která odebere pozorovatele pro AVPlayerItem.DidPlayToEndTimeNotification oznámení, a který také vyvolá metodu DisposeNSObjectna .

Okna

Následující příklad kódu ukazuje, jak UpdateIsLooping metoda ve Windows umožňuje smyčku videa:

public void UpdateIsLooping()
{
    if (_isMediaPlayerAttached)
        _mediaPlayerElement.MediaPlayer.IsLoopingEnabled = _video.IsLooping;
}

Chcete-li povolit smyčku videa, UpdateIsLooping metoda nastaví MediaPlayerElement.MediaPlayer.IsLoopingEnabled vlastnost na hodnotu Video.IsLooping vlastnosti.

Vytvoření vlastních ovládacích prvků přenosu

Ovládací prvky přenosu přehrávače videa zahrnují tlačítka, která přehrají, pozastaví a zastaví video. Tato tlačítka jsou často identifikována známými ikonami místo textu a tlačítka přehrávání a pozastavení se často kombinují do jednoho tlačítka.

Ve výchozím nastavení Video ovládací prvek zobrazuje ovládací prvky přenosu podporované jednotlivými platformami. Pokud však nastavíte AreTransportControlsEnabled vlastnost na false, tyto ovládací prvky jsou potlačeny. Přehrávání videa pak můžete ovládat programově nebo zadat vlastní ovládací prvky přenosu.

Implementace vlastních ovládacích prvků přenosu vyžaduje, Video aby třída mohla informovat své nativní zobrazení, aby video přehrála, pozastavila nebo zastavila, a věděla aktuální stav přehrávání videa. Třída Video definuje metody s názvem Play, Pausea Stop které vyvolat odpovídající událost a odeslat příkaz do VideoHandler:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        public event EventHandler<VideoPositionEventArgs> PlayRequested;
        public event EventHandler<VideoPositionEventArgs> PauseRequested;
        public event EventHandler<VideoPositionEventArgs> StopRequested;

        public void Play()
        {
            VideoPositionEventArgs args = new VideoPositionEventArgs(Position);
            PlayRequested?.Invoke(this, args);
            Handler?.Invoke(nameof(Video.PlayRequested), args);
        }

        public void Pause()
        {
            VideoPositionEventArgs args = new VideoPositionEventArgs(Position);
            PauseRequested?.Invoke(this, args);
            Handler?.Invoke(nameof(Video.PauseRequested), args);
        }

        public void Stop()
        {
            VideoPositionEventArgs args = new VideoPositionEventArgs(Position);
            StopRequested?.Invoke(this, args);
            Handler?.Invoke(nameof(Video.StopRequested), args);
        }
    }
}

Třída VideoPositionEventArgs definuje Position vlastnost, kterou lze nastavit prostřednictvím jeho konstruktoru. Tato vlastnost představuje pozici, ve které bylo přehrávání videa spuštěno, pozastaveno nebo zastaveno.

Poslední řádek v , PlayPausea Stop metody odešle příkaz a přidružená data do VideoHandler. VideoHandler Názvy CommandMapper příkazů mapují na akce, které se spustí při přijetí příkazu. Například když VideoHandler obdrží PlayRequested příkaz, spustí jeho MapPlayRequested metodu. Výhodou tohoto přístupu je, že eliminuje potřebu nativních zobrazení k odběru a odhlášení odběru událostí řízení napříč platformami. Kromě toho umožňuje snadné přizpůsobení, protože mapovač příkazů je možné upravit příjemci řízení napříč platformami bez podtřídy. Další informace o CommandMappernástroji mapper naleznete v tématu Vytvoření mapovače příkazů.

Implementace MauiVideoPlayer v Androidu, iOS a Mac Catalyst, má PlayRequested, PauseRequesteda StopRequested metody, které jsou prováděny v reakci na Video ovládací prvek odesílání PlayRequested, PauseRequesteda StopRequested příkazy. Každá metoda vyvolá metodu v nativním zobrazení pro přehrávání, pozastavení nebo zastavení videa. Například následující kód ukazuje PlayRequested, PauseRequesteda StopRequested metody v iOS a Mac Catalyst:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        ...

        public void PlayRequested(TimeSpan position)
        {
            _player.Play();
            Debug.WriteLine($"Video playback from {position.Hours:X2}:{position.Minutes:X2}:{position.Seconds:X2}.");
        }

        public void PauseRequested(TimeSpan position)
        {
            _player.Pause();
            Debug.WriteLine($"Video paused at {position.Hours:X2}:{position.Minutes:X2}:{position.Seconds:X2}.");
        }

        public void StopRequested(TimeSpan position)
        {
            _player.Pause();
            _player.Seek(new CMTime(0, 1));
            Debug.WriteLine($"Video stopped at {position.Hours:X2}:{position.Minutes:X2}:{position.Seconds:X2}.");
        }
    }
}

Každá ze tří metod zaznamenává polohu přehrávání, pozastavení nebo zastavení videa pomocí dat odesílaných pomocí příkazu.

Tento mechanismus zajišťuje, že při Playvyvolání , Pausenebo Stop metody na Video ovládacím prvku, jeho nativní zobrazení je instruováno k přehrávání, pozastavení nebo zastavení videa a protokolování pozice, ve které bylo video přehráno, pozastaveno nebo zastaveno. K tomu dochází pomocí odděleného přístupu, aniž by se nativní zobrazení musela přihlásit k odběru událostí napříč platformami.

Stav videa

Implementace funkce přehrávání, pozastavení a zastavení nestačí pro podporu vlastních ovládacích prvků přenosu. Funkce přehrávání a pozastavení by se často měla implementovat se stejným tlačítkem, což změní vzhled, aby bylo možné určit, jestli se video právě přehrává nebo pozastavuje. Tlačítko by navíc nemělo být povolené ani v případě, že se video ještě nenačetlo.

Tyto požadavky naznačují, že přehrávač videa musí zpřístupnit aktuální stav označující, jestli se přehrává nebo pozastavuje, nebo pokud ještě není připravený k přehrávání videa. Tento stav může reprezentovat výčet:

public enum VideoStatus
{
    NotReady,
    Playing,
    Paused
}

Třída Video definuje vlastnost bindable jen pro čtení s názvem Status typu VideoStatus. Tato vlastnost je definována jako jen pro čtení, protože by měla být nastavena pouze z obslužné rutiny ovládacího prvku:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        private static readonly BindablePropertyKey StatusPropertyKey =
            BindableProperty.CreateReadOnly(nameof(Status), typeof(VideoStatus), typeof(Video), VideoStatus.NotReady);

        public static readonly BindableProperty StatusProperty = StatusPropertyKey.BindableProperty;

        public VideoStatus Status
        {
            get { return (VideoStatus)GetValue(StatusProperty); }
        }

        VideoStatus IVideoController.Status
        {
            get { return Status; }
            set { SetValue(StatusPropertyKey, value); }
        }
        ...
    }
}

Obvykle by vlastnost bindable jen pro čtení měla privátní set přístup k Status vlastnosti, aby bylo možné ji nastavit z třídy. U derivátů View podporovaných obslužnými rutinami však musí být vlastnost nastavena zvnějšku třídy, ale pouze obslužnou rutinou ovládacího prvku.

Z tohoto důvodu je definována další vlastnost s názvem IVideoController.Status. Jedná se o explicitní implementaci rozhraní a je možné rozhraním IVideoController , které Video třída implementuje:

public interface IVideoController
{
    VideoStatus Status { get; set; }
    TimeSpan Duration { get; set; }
}

Toto rozhraní umožňuje externí Video třídě nastavit Status vlastnost odkazem na IVideoController rozhraní. Vlastnost lze nastavit z jiných tříd a obslužné rutiny, ale není pravděpodobné, že by byla neúmyslně nastavena. Nejdůležitější je, že Status vlastnost nejde nastavit prostřednictvím datové vazby.

Aby se usnadnila implementace obslužné rutiny při zachování Status aktualizace vlastnosti, Video třída definuje UpdateStatus událost a příkaz:

using System.ComponentModel;

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        public event EventHandler UpdateStatus;

        IDispatcherTimer _timer;

        public Video()
        {
            _timer = Dispatcher.CreateTimer();
            _timer.Interval = TimeSpan.FromMilliseconds(100);
            _timer.Tick += OnTimerTick;
            _timer.Start();
        }

        ~Video() => _timer.Tick -= OnTimerTick;

        void OnTimerTick(object sender, EventArgs e)
        {
            UpdateStatus?.Invoke(this, EventArgs.Empty);
            Handler?.Invoke(nameof(Video.UpdateStatus));
        }
        ...
    }
}

Obslužná OnTimerTick rutina události se spustí každou desátou sekundu, která UpdateStatus vyvolá událost a vyvolá UpdateStatus příkaz.

UpdateStatus Když je příkaz odeslán z Video ovládacího prvku do jeho obslužné rutiny, příkaz mapper obslužné rutiny zajistí, že MapUpdateStatus metoda je vyvolána:

public static void MapUpdateStatus(VideoHandler handler, Video video, object? args)
{
    handler.PlatformView?.UpdateStatus();
}

Metoda MapUpdateStatus pak volá metodu UpdateStatus ve vlastnosti obslužné rutiny PlatformView . Vlastnost PlatformView , která je typu MauiVideoPlayer, zapouzdřuje nativní zobrazení, která poskytují implementaci přehrávače videa na každé platformě.

Android

Následující příklad kódu ukazuje metodu UpdateStatus v Androidu Status nastaví vlastnost:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        bool _isPrepared;
        Video _video;
        ...

        public MauiVideoPlayer(Context context, Video video) : base(context)
        {
            _video = video;
            ...
            _videoView.Prepared += OnVideoViewPrepared;
        }

        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                _videoView.Prepared -= OnVideoViewPrepared;
                ...
            }

            base.Dispose(disposing);
        }

        void OnVideoViewPrepared(object sender, EventArgs args)
        {
            _isPrepared = true;
            ((IVideoController)_video).Duration = TimeSpan.FromMilliseconds(_videoView.Duration);
        }

        public void UpdateStatus()
        {
            VideoStatus status = VideoStatus.NotReady;

            if (_isPrepared)
                status = _videoView.IsPlaying ? VideoStatus.Playing : VideoStatus.Paused;

            ((IVideoController)_video).Status = status;
            ...
        }
        ...
    }
}

Vlastnost VideoView.IsPlaying je logická hodnota, která označuje, jestli se video přehrává nebo pozastavuje. Pokud chcete zjistit, jestli VideoView video nejde přehrát nebo pozastavit, musí se zpracovat jeho Prepared událost. Tato událost se vyvolá, když je zdroj médií připravený k přehrávání. Událost se přihlásí k odběru v konstruktoru MauiVideoPlayer a v Dispose jejím přepsání se odhlásí. Metoda UpdateStatus pak použije isPrepared pole a VideoView.IsPlaying vlastnost k nastavení Status vlastnosti objektu Video jeho přetypováním na IVideoController.

iOS a Mac Catalyst

Následující příklad kódu ukazuje metodu UpdateStatus v iOS a Mac Catalyst nastaví Status vlastnost:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        Video _video;
        ...

        public void UpdateStatus()
        {
            VideoStatus videoStatus = VideoStatus.NotReady;

            switch (_player.Status)
            {
                case AVPlayerStatus.ReadyToPlay:
                    switch (_player.TimeControlStatus)
                    {
                        case AVPlayerTimeControlStatus.Playing:
                            videoStatus = VideoStatus.Playing;
                            break;

                        case AVPlayerTimeControlStatus.Paused:
                            videoStatus = VideoStatus.Paused;
                            break;
                    }
                    break;
            }
            ((IVideoController)_video).Status = videoStatus;
            ...
        }
        ...
    }
}

K nastavení vlastnosti - Status vlastnosti typu AVPlayerStatus a vlastnosti typu AVPlayerTimeControlStatusje třeba získat přístup ke dvěma vlastnostem AVPlayerTimeControlStatus .Status Vlastnost Status pak lze nastavit na Video objekt přetypováním na IVideoController.

Okna

Následující příklad kódu ukazuje metodu UpdateStatus ve Windows nastaví Status vlastnost:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        public void UpdateStatus()
        {
            if (_isMediaPlayerAttached)
            {
                VideoStatus status = VideoStatus.NotReady;

                switch (_mediaPlayerElement.MediaPlayer.CurrentState)
                {
                    case MediaPlayerState.Playing:
                        status = VideoStatus.Playing;
                        break;
                    case MediaPlayerState.Paused:
                    case MediaPlayerState.Stopped:
                        status = VideoStatus.Paused;
                        break;
                }

                ((IVideoController)_video).Status = status;
                _video.Position = _mediaPlayerElement.MediaPlayer.Position;
            }
        }
        ...
    }
}

Metoda UpdateStatus používá hodnotu MediaPlayerElement.MediaPlayer.CurrentState vlastnosti k určení hodnoty Status vlastnosti. Vlastnost Status pak lze nastavit na Video objekt přetypováním na IVideoController.

Umístění pruhu

Ovládací prvky přenosu implementované jednotlivými platformami zahrnují poziční pruh. Tento pruh se podobá posuvníku nebo posuvníku a zobrazuje aktuální umístění videa během jeho celkové doby trvání. Uživatelé můžou manipulovat s panelem umístění a pohybovat se dopředu nebo dozadu na novou pozici ve videu.

Implementace vlastního panelu umístění vyžaduje, Video aby třída věděla dobu trvání videa a její aktuální pozici v rámci této doby trvání.

Doba trvání

Jedna položka informací, kterou Video ovládací prvek potřebuje pro podporu vlastního panelu umístění, je doba trvání videa. Třída Video definuje vlastnost bindable jen pro čtení s názvem Duration, typu TimeSpan. Tato vlastnost je definována jako jen pro čtení, protože by měla být nastavena pouze z obslužné rutiny ovládacího prvku:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        private static readonly BindablePropertyKey DurationPropertyKey =
            BindableProperty.CreateReadOnly(nameof(Duration), typeof(TimeSpan), typeof(Video), new TimeSpan(),
                propertyChanged: (bindable, oldValue, newValue) => ((Video)bindable).SetTimeToEnd());

        public static readonly BindableProperty DurationProperty = DurationPropertyKey.BindableProperty;

        public TimeSpan Duration
        {
            get { return (TimeSpan)GetValue(DurationProperty); }
        }

        TimeSpan IVideoController.Duration
        {
            get { return Duration; }
            set { SetValue(DurationPropertyKey, value); }
        }
        ...
    }
}

Obvykle by vlastnost bindable jen pro čtení měla privátní set přístup k Duration vlastnosti, aby bylo možné ji nastavit z třídy. U derivátů View podporovaných obslužnými rutinami však musí být vlastnost nastavena zvnějšku třídy, ale pouze obslužnou rutinou ovládacího prvku.

Poznámka:

Obslužná rutina události změněná vlastností vazby Duration volá metodu s názvem SetTimeToEnd, která je popsána v výpočtu času na konec.

Z tohoto důvodu je definována další vlastnost s názvem IVideoController.Duration. Jedná se o explicitní implementaci rozhraní a je možné rozhraním IVideoController , které Video třída implementuje:

public interface IVideoController
{
    VideoStatus Status { get; set; }
    TimeSpan Duration { get; set; }
}

Toto rozhraní umožňuje externí Video třídě nastavit Duration vlastnost odkazem na IVideoController rozhraní. Vlastnost lze nastavit z jiných tříd a obslužné rutiny, ale není pravděpodobné, že by byla neúmyslně nastavena. Nejdůležitější je, že Duration vlastnost nejde nastavit prostřednictvím datové vazby.

Doba trvání videa není k dispozici okamžitě po Source nastavení vlastnosti Video ovládacího prvku. Před určením doby trvání nativního zobrazení musí být video částečně staženo.

Android

V Androidu vlastnost VideoView.Duration hlásí platnou dobu trvání v milisekundách po VideoView.Prepared vyvolání události. Třída MauiVideoPlayer používá obslužnou rutinu Prepared události k získání Duration hodnoty vlastnosti:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        Video _video;
        ...

        void OnVideoViewPrepared(object sender, EventArgs args)
        {
            ...
            ((IVideoController)_video).Duration = TimeSpan.FromMilliseconds(_videoView.Duration);
        }
        ...
    }
}
iOS a Mac Catalyst

Na iOS a Mac Catalyst, doba trvání videa je získána z AVPlayerItem.Duration vlastnosti, ale ne okamžitě po AVPlayerItem vytvoření. Pro vlastnost je možné nastavit pozorovatele iOS Duration , ale MauiVideoPlayer třída získá dobu trvání v UpdateStatus metodě, která se nazývá 10krát za sekundu:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayerItem _playerItem;
        ...

        TimeSpan ConvertTime(CMTime cmTime)
        {
            return TimeSpan.FromSeconds(Double.IsNaN(cmTime.Seconds) ? 0 : cmTime.Seconds);
        }

        public void UpdateStatus()
        {
            ...
            if (_playerItem != null)
            {
                ((IVideoController)_video).Duration = ConvertTime(_playerItem.Duration);
                ...
            }
        }
        ...
    }
}

Metoda ConvertTime převede CMTime objekt na TimeSpan hodnotu.

Okna

Ve Windows je TimeSpan vlastnost hodnota, MediaPlayerElement.MediaPlayer.NaturalDuration která se stane platnou MediaPlayerElement.MediaPlayer.MediaOpened při vyvolání události. Třída MauiVideoPlayer používá obslužnou rutinu MediaOpened události k získání NaturalDuration hodnoty vlastnosti:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        void OnMediaPlayerMediaOpened(MediaPlayer sender, object args)
        {
            MainThread.BeginInvokeOnMainThread(() =>
            {
                ((IVideoController)_video).Duration = _mediaPlayerElement.MediaPlayer.NaturalDuration;
            });
        }
        ...
    }
}

Obslužná rutina OnMediaPlayer události pak volá metodu MainThread.BeginInvokeOnMainThread nastavit Duration vlastnost objektu Video , přetypováním na IVideoController, v hlavním vlákně. To je nezbytné, protože MediaPlayerElement.MediaPlayer.MediaOpened událost se zpracovává ve vlákně na pozadí. Další informace o spuštění kódu v hlavním vlákně naleznete v tématu Vytvoření vlákna ve vlákně uživatelského rozhraní .NET MAUI.

Position

Ovládací Video prvek také potřebuje Position vlastnost, která se při přehrávání videa zvýší z nuly na Duration hodnotu. Třída Video implementuje tuto vlastnost jako bindable vlastnost s veřejnými get a set přístupovými objekty:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        public static readonly BindableProperty PositionProperty =
            BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(Video), new TimeSpan(),
                propertyChanged: (bindable, oldValue, newValue) => ((Video)bindable).SetTimeToEnd());

        public TimeSpan Position
        {
            get { return (TimeSpan)GetValue(PositionProperty); }
            set { SetValue(PositionProperty, value); }
        }
        ...
    }
}

Přistupovací get objekt vrátí aktuální pozici videa při přehrávání. Přistupující set objekt reaguje na manipulaci uživatele s polohovacím pruhem přesunutím pozice videa dopředu nebo dozadu.

Poznámka:

Obslužná rutina události změněná vlastností vazby Position volá metodu s názvem SetTimeToEnd, která je popsána v výpočtu času na konec.

V Androidu, iOS a Mac Catalyst má vlastnost, která získá aktuální pozici pouze get příslušenství. Místo toho je k dispozici metoda pro Seek nastavení pozice. Zdá se, že je to rozumnější přístup než použití jediné Position vlastnosti, která má vlastní problém. Při přehrávání videa se vlastnost musí průběžně aktualizovat tak, Position aby odrážela novou pozici. Ale nechcete, aby většina změn Position vlastnosti způsobila, že se přehrávač videa přesune na nové místo ve videu. Pokud k tomu dojde, přehrávač videa by reagoval vyhledáním poslední hodnoty Position vlastnosti a video by nepřešlo.

I přes potíže s implementací Position vlastnosti s get a set přístupovými objekty se tento přístup používá, protože může využívat datové vazby. Vlastnost PositionVideo ovládacího prvku může být svázána s objektem Slider , který slouží k zobrazení pozice i k vyhledání nové pozice. Při implementaci Position vlastnosti je však nutné provést několik opatření, aby se zabránilo smyčkám zpětné vazby.

Android

V Androidu VideoView.CurrentPosition tato vlastnost označuje aktuální pozici videa. Třída MauiVideoPlayer nastaví Position vlastnost v UpdateStatus metodě současně s tím, jak nastaví Duration vlastnost:

using Android.Content;
using Android.Views;
using Android.Widget;
using AndroidX.CoordinatorLayout.Widget;
using VideoDemos.Controls;
using Color = Android.Graphics.Color;
using Uri = Android.Net.Uri;

namespace VideoDemos.Platforms.Android
{
    public class MauiVideoPlayer : CoordinatorLayout
    {
        VideoView _videoView;
        Video _video;
        ...

        public void UpdateStatus()
        {
            ...
            TimeSpan timeSpan = TimeSpan.FromMilliseconds(_videoView.CurrentPosition);
            _video.Position = timeSpan;
        }

        public void UpdatePosition()
        {
            if (Math.Abs(_videoView.CurrentPosition - _video.Position.TotalMilliseconds) > 1000)
            {
                _videoView.SeekTo((int)_video.Position.TotalMilliseconds);
            }
        }
        ...
    }
}

Pokaždé, Position když je vlastnost nastavena UpdateStatus metodou, Position vlastnost aktivuje PropertyChanged událost, což způsobí mapovač vlastnosti pro obslužnou rutinu UpdatePosition volání metody. Metoda UpdatePosition by neměla dělat nic pro většinu změn vlastnosti. Jinak by se při každé změně pozice videa přesunula na stejnou pozici, na které právě dosáhl. Chcete-li se této smyčce zpětné vazby vyhnout, UpdatePosition jediný volá metodu Seek objektu VideoView , pokud je rozdíl mezi Position vlastností a aktuální pozicí objektu VideoView větší než jedna sekunda.

iOS a Mac Catalyst

Na iOS a Mac Catalyst, AVPlayerItem.CurrentTime vlastnost označuje aktuální pozici videa. Třída MauiVideoPlayer nastaví Position vlastnost v UpdateStatus metodě současně s tím, jak nastaví Duration vlastnost:

using AVFoundation;
using AVKit;
using CoreMedia;
using Foundation;
using System.Diagnostics;
using UIKit;
using VideoDemos.Controls;

namespace VideoDemos.Platforms.MaciOS
{
    public class MauiVideoPlayer : UIView
    {
        AVPlayer _player;
        AVPlayerItem _playerItem;
        Video _video;
        ...

        TimeSpan ConvertTime(CMTime cmTime)
        {
            return TimeSpan.FromSeconds(Double.IsNaN(cmTime.Seconds) ? 0 : cmTime.Seconds);
        }

        public void UpdateStatus()
        {
            ...
            if (_playerItem != null)
            {
                ...
                _video.Position = ConvertTime(_playerItem.CurrentTime);
            }
        }

        public void UpdatePosition()
        {
            TimeSpan controlPosition = ConvertTime(_player.CurrentTime);
            if (Math.Abs((controlPosition - _video.Position).TotalSeconds) > 1)
            {
                _player.Seek(CMTime.FromSeconds(_video.Position.TotalSeconds, 1));
            }
        }
        ...
    }
}

Pokaždé, Position když je vlastnost nastavena UpdateStatus metodou, Position vlastnost aktivuje PropertyChanged událost, což způsobí mapovač vlastnosti pro obslužnou rutinu UpdatePosition volání metody. Metoda UpdatePosition by neměla dělat nic pro většinu změn vlastnosti. Jinak by se při každé změně pozice videa přesunula na stejnou pozici, na které právě dosáhl. Chcete-li se této smyčce zpětné vazby vyhnout, UpdatePosition jediný volá metodu Seek objektu AVPlayer , pokud je rozdíl mezi Position vlastností a aktuální pozicí objektu AVPlayer větší než jedna sekunda.

Okna

Ve Windows tato MediaPlayerElement.MedaPlayer.Position vlastnost označuje aktuální pozici videa. Třída MauiVideoPlayer nastaví Position vlastnost v UpdateStatus metodě současně s tím, jak nastaví Duration vlastnost:

using Microsoft.UI.Xaml.Controls;
using VideoDemos.Controls;
using Windows.Media.Core;
using Windows.Media.Playback;
using Windows.Storage;
using Grid = Microsoft.UI.Xaml.Controls.Grid;

namespace VideoDemos.Platforms.Windows
{
    public class MauiVideoPlayer : Grid, IDisposable
    {
        MediaPlayerElement _mediaPlayerElement;
        Video _video;
        bool _isMediaPlayerAttached;
        ...

        public void UpdateStatus()
        {
            if (_isMediaPlayerAttached)
            {
                ...
                _video.Position = _mediaPlayerElement.MediaPlayer.Position;
            }
        }

        public void UpdatePosition()
        {
            if (_isMediaPlayerAttached)
            {
                if (Math.Abs((_mediaPlayerElement.MediaPlayer.Position - _video.Position).TotalSeconds) > 1)
                {
                    _mediaPlayerElement.MediaPlayer.Position = _video.Position;
                }
            }
        }
        ...
    }
}

Pokaždé, Position když je vlastnost nastavena UpdateStatus metodou, Position vlastnost aktivuje PropertyChanged událost, což způsobí mapovač vlastnosti pro obslužnou rutinu UpdatePosition volání metody. Metoda UpdatePosition by neměla dělat nic pro většinu změn vlastnosti. Jinak by se při každé změně pozice videa přesunula na stejnou pozici, na které právě dosáhl. Pokud se chcete této smyčce zpětné vazby vyhnout, nastaví se vlastnost pouze MediaPlayerElement.MediaPlayer.Position v případě, UpdatePosition že je rozdíl mezi Position vlastností a aktuální polohou MediaPlayerElement větší než jedna sekunda.

Výpočet času na konec

Někdy video přehrávač zobrazuje čas zbývající ve videu. Tato hodnota začíná dobou trvání videa při zahájení videa a po skončení videa se sníží na nulu.

Třída Video obsahuje vlastnost jen TimeToEnd pro čtení, která se počítá na základě změn Duration a Position vlastností:

namespace VideoDemos.Controls
{
    public class Video : View, IVideoController
    {
        ...
        private static readonly BindablePropertyKey TimeToEndPropertyKey =
            BindableProperty.CreateReadOnly(nameof(TimeToEnd), typeof(TimeSpan), typeof(Video), new TimeSpan());

        public static readonly BindableProperty TimeToEndProperty = TimeToEndPropertyKey.BindableProperty;

        public TimeSpan TimeToEnd
        {
            get { return (TimeSpan)GetValue(TimeToEndProperty); }
            private set { SetValue(TimeToEndPropertyKey, value); }
        }

        void SetTimeToEnd()
        {
            TimeToEnd = Duration - Position;
        }
        ...
    }
}

Metoda SetTimeToEnd je volána z obslužných Duration rutin událostí změněné vlastností a Position vlastností.

Vlastní panel umístění

Vlastní poziční pruh lze implementovat vytvořením třídy, která je odvozena od Slider, která obsahuje Duration a Position vlastnosti typu TimeSpan:

namespace VideoDemos.Controls
{
    public class PositionSlider : Slider
    {
        public static readonly BindableProperty DurationProperty =
            BindableProperty.Create(nameof(Duration), typeof(TimeSpan), typeof(PositionSlider), new TimeSpan(1),
                propertyChanged: (bindable, oldValue, newValue) =>
                {
                    double seconds = ((TimeSpan)newValue).TotalSeconds;
                    ((Slider)bindable).Maximum = seconds <= 0 ? 1 : seconds;
                });

        public static readonly BindableProperty PositionProperty =
            BindableProperty.Create(nameof(Position), typeof(TimeSpan), typeof(PositionSlider), new TimeSpan(0),
                defaultBindingMode: BindingMode.TwoWay,
                propertyChanged: (bindable, oldValue, newValue) =>
                {
                    double seconds = ((TimeSpan)newValue).TotalSeconds;
                    ((Slider)bindable).Value = seconds;
                });

        public TimeSpan Duration
        {
            get { return (TimeSpan)GetValue(DurationProperty); }
            set { SetValue(DurationProperty, value); }
        }

        public TimeSpan Position
        {
            get { return (TimeSpan)GetValue(PositionProperty); }
            set { SetValue (PositionProperty, value); }
        }

        public PositionSlider()
        {
            PropertyChanged += (sender, args) =>
            {
                if (args.PropertyName == "Value")
                {
                    TimeSpan newPosition = TimeSpan.FromSeconds(Value);
                    if (Math.Abs(newPosition.TotalSeconds - Position.TotalSeconds) / Duration.TotalSeconds > 0.01)
                        Position = newPosition;
                }
            };
        }
    }
}

Obslužná rutina události změněná vlastností pro Duration vlastnost nastaví Maximum vlastnost SliderTotalSecondsTimeSpan vlastnosti hodnoty. Podobně obslužná rutina události změněné vlastností pro Position vlastnost nastaví Value vlastnost Slider. Jedná se o mechanismus, kterým Slider sleduje pozici PositionSlider.

Aktualizuje PositionSlider se z podkladu Slider pouze v jednom scénáři, což je situace, kdy uživatel manipuluje Slider s uvedením, že video by mělo být rozšířené nebo obrácené na novou pozici. Tato hodnota je zjištěna v PropertyChanged obslužné rutině v konstruktoru PositionSlider . Tato obslužná rutina události kontroluje změnu vlastnosti Value a pokud se liší od Position vlastnosti, Position vlastnost je nastavena Value z vlastnosti.

Registrace obslužné rutiny

Před použitím vlastního ovládacího prvku a jeho obslužné rutiny musí být registrovány v aplikaci. K tomu by mělo dojít v CreateMauiApp metodě ve MauiProgram třídě v projektu aplikace, což je vstupní bod aplikace pro různé platformy:

using VideoDemos.Controls;
using VideoDemos.Handlers;

namespace VideoDemos;

public static class MauiProgram
{
    public static MauiApp CreateMauiApp()
    {
        var builder = MauiApp.CreateBuilder();
        builder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                fonts.AddFont("OpenSans-Semibold.ttf", "OpenSansSemibold");
            })
            .ConfigureMauiHandlers(handlers =>
            {
                handlers.AddHandler(typeof(Video), typeof(VideoHandler));
            });

        return builder.Build();
    }
}

Obslužná rutina je zaregistrována pomocí ConfigureMauiHandlers metody a AddHandler metody. Prvním argumentem AddHandler metody je typ ovládacího prvku pro různé platformy, přičemž druhým argumentem je jeho typ obslužné rutiny.

Využívání multiplatformních ovládacích prvků

Po registraci obslužné rutiny v aplikaci je možné využívat řízení napříč platformami.

Přehrávání webového videa

Ovládací Video prvek může přehrát video z adresy URL, jak je znázorněno v následujícím příkladu:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.PlayWebVideoPage"
             Unloaded="OnContentPageUnloaded"
             Title="Play web video">
    <controls:Video x:Name="video"
                    Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />
</ContentPage>

V tomto příkladu VideoSourceConverter třída převede řetězec, který představuje identifikátor URI na .UriVideoSource Video se pak začne načítat a začne přehrávat, jakmile se stáhne a do vyrovnávací paměti dostatek dat. Na každé platformě zmizí ovládací prvky přenosu, pokud se nepoužívají, ale je možné je obnovit klepnutím na video.

Přehrání prostředku videa

Videosoubory, které jsou vložené do složky Resources\Raw aplikace, s akcí sestavení MauiAsset , lze přehrát ovládacím Video prvku:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.PlayVideoResourcePage"
             Unloaded="OnContentPageUnloaded"
             Title="Play video resource">
    <controls:Video x:Name="video"
                    Source="video.mp4" />
</ContentPage>

V tomto příkladu VideoSourceConverter třída převede řetězec, který představuje název souboru videa na .ResourceVideoSource Pro každou platformu se video začne přehrávat téměř okamžitě po nastavení zdroje videa, protože soubor je v balíčku aplikace a není potřeba ho stáhnout. Na každé platformě zmizí ovládací prvky přenosu, pokud se nepoužívají, ale je možné je obnovit klepnutím na video.

Přehrání videosouboru z knihovny zařízení

Videosoubory, které jsou uložené na zařízení, se dají načíst a pak přehrát ovládacím Video prvku:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.PlayLibraryVideoPage"
             Unloaded="OnContentPageUnloaded"
             Title="Play library video">
    <Grid RowDefinitions="*,Auto">
        <controls:Video x:Name="video" />
        <Button Grid.Row="1"
                Text="Show Video Library"
                Margin="10"
                HorizontalOptions="Center"
                Clicked="OnShowVideoLibraryClicked" />
    </Grid>
</ContentPage>

Po klepnutí na obslužnou Clicked rutinu Button události se spustí, což je znázorněno v následujícím příkladu kódu:

async void OnShowVideoLibraryClicked(object sender, EventArgs e)
{
    Button button = sender as Button;
    button.IsEnabled = false;

    var pickedVideo = await MediaPicker.PickVideoAsync();
    if (!string.IsNullOrWhiteSpace(pickedVideo?.FileName))
    {
        video.Source = new FileVideoSource
        {
            File = pickedVideo.FullPath
        };
    }

    button.IsEnabled = true;
}

Obslužná rutina Clicked události používá třídu .NET MAUI MediaPicker k tomu, aby uživatel vybral videosoubor ze zařízení. Vybraný videosoubor je pak zapouzdřen jako FileVideoSource objekt a nastaven jako Source vlastnost Video ovládacího prvku. Další informace o MediaPicker třídě naleznete v tématu Výběr médií. Pro každou platformu se video začne přehrávat téměř okamžitě po nastavení zdroje videa, protože soubor je na zařízení a není potřeba ho stáhnout. Na každé platformě zmizí ovládací prvky přenosu, pokud se nepoužívají, ale je možné je obnovit klepnutím na video.

Konfigurace ovládacího prvku Video

Můžete zabránit automatickému spuštění videa nastavením AutoPlay vlastnosti na false:

<controls:Video x:Name="video"
                Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
                AutoPlay="False" />

Ovládací prvky přenosu můžete potlačit nastavením AreTransportControlsEnabled vlastnosti na false:

<controls:Video x:Name="video"
                Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
                AreTransportControlsEnabled="False" />

Pokud nastavíte a AreTransportControlsEnabled nastavíte AutoPlayfalse, video se nezačne přehrávat a nebude možné ho spustit. V tomto scénáři byste museli volat metodu Play ze souboru kódu nebo vytvořit vlastní ovládací prvky přenosu.

Kromě toho můžete video nastavit na smyčku nastavením IsLooping vlastnosti na true:

<controls:Video x:Name="video"
                Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4"
                IsLooping="true" />

Pokud vlastnost nastavíte IsLooping tak true , že Video ovládací prvek automaticky nastaví pozici videa na začátek po dosažení jeho konce.

Použití vlastních ovládacích prvků přenosu

Následující příklad XAML ukazuje vlastní ovládací prvky přenosu, které přehrávají, pozastavují a zastavují video:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.CustomTransportPage"
             Unloaded="OnContentPageUnloaded"
             Title="Custom transport controls">
    <Grid RowDefinitions="*,Auto">
        <controls:Video x:Name="video"
                        AutoPlay="False"
                        AreTransportControlsEnabled="False"
                        Source="https://archive.org/download/BigBuckBunny_328/BigBuckBunny_512kb.mp4" />
        <ActivityIndicator Color="Gray"
                           IsVisible="False">
            <ActivityIndicator.Triggers>
                <DataTrigger TargetType="ActivityIndicator"
                             Binding="{Binding Source={x:Reference video},
                                               Path=Status}"
                             Value="{x:Static controls:VideoStatus.NotReady}">
                    <Setter Property="IsVisible"
                            Value="True" />
                    <Setter Property="IsRunning"
                            Value="True" />
                </DataTrigger>
            </ActivityIndicator.Triggers>
        </ActivityIndicator>
        <Grid Grid.Row="1"
              Margin="0,10"
              ColumnDefinitions="0.5*,0.5*"
              BindingContext="{x:Reference video}">
            <Button Text="&#x25B6;&#xFE0F; Play"
                    HorizontalOptions="Center"
                    Clicked="OnPlayPauseButtonClicked">
                <Button.Triggers>
                    <DataTrigger TargetType="Button"
                                 Binding="{Binding Status}"
                                 Value="{x:Static controls:VideoStatus.Playing}">
                        <Setter Property="Text"
                                Value="&#x23F8; Pause" />
                    </DataTrigger>
                    <DataTrigger TargetType="Button"
                                 Binding="{Binding Status}"
                                 Value="{x:Static controls:VideoStatus.NotReady}">
                        <Setter Property="IsEnabled"
                                Value="False" />
                    </DataTrigger>
                </Button.Triggers>
            </Button>
            <Button Grid.Column="1"
                    Text="&#x23F9; Stop"
                    HorizontalOptions="Center"
                    Clicked="OnStopButtonClicked">
                <Button.Triggers>
                    <DataTrigger TargetType="Button"
                                 Binding="{Binding Status}"
                                 Value="{x:Static controls:VideoStatus.NotReady}">
                        <Setter Property="IsEnabled"
                                Value="False" />
                    </DataTrigger>
                </Button.Triggers>
            </Button>
        </Grid>
    </Grid>
</ContentPage>

V tomto příkladu VideoAreTransportControlsEnabled ovládací prvek nastaví vlastnost na false a definuje Button , který se přehraje a pozastaví video a Button které zastaví přehrávání videa. Vzhled tlačítka se definuje pomocí znaků Unicode a jejich textových ekvivalentů k vytvoření tlačítek, která se skládají z ikony a textu:

Screenshot of play and pause buttons.

Když se video přehrává, tlačítko přehrát se aktualizuje na tlačítko pozastavit:

Screenshot of pause and stop buttons.

Uživatelské rozhraní obsahuje ActivityIndicator také zobrazení, které se zobrazí při načítání videa. Aktivační události dat slouží k povolení a zakázání ActivityIndicator tlačítek a k přepnutí prvního tlačítka mezi přehráváním a pozastavením. Další informace o aktivačních událostech dat najdete v tématu Aktivační události dat.

Soubor kódu za kódem definuje obslužné rutiny událostí pro události tlačítka Clicked :

public partial class CustomTransportPage : ContentPage
{
    ...
    void OnPlayPauseButtonClicked(object sender, EventArgs args)
    {
        if (video.Status == VideoStatus.Playing)
        {
            video.Pause();
        }
        else if (video.Status == VideoStatus.Paused)
        {
            video.Play();
        }
    }

    void OnStopButtonClicked(object sender, EventArgs args)
    {
        video.Stop();
    }
    ...
}

Vlastní panel umístění

Následující příklad ukazuje vlastní panel umístění , PositionSliderkterý se spotřebovává v XAML:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             x:Class="VideoDemos.Views.CustomPositionBarPage"
             Unloaded="OnContentPageUnloaded"
             Title="Custom position bar">
    <Grid RowDefinitions="*,Auto,Auto">
        <controls:Video x:Name="video"
                        AreTransportControlsEnabled="False"
                        Source="{StaticResource ElephantsDream}" />
        ...
        <Grid Grid.Row="1"
              Margin="10,0"
              ColumnDefinitions="0.25*,0.25*,0.25*,0.25*"
              BindingContext="{x:Reference video}">
            <Label Text="{Binding Path=Position,
                                  StringFormat='{0:hh\\:mm\\:ss}'}"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
            ...
            <Label Grid.Column="3"
                   Text="{Binding Path=TimeToEnd,
                                  StringFormat='{0:hh\\:mm\\:ss}'}"
                   HorizontalOptions="Center"
                   VerticalOptions="Center" />
        </Grid>
        <controls:PositionSlider Grid.Row="2"
                                 Margin="10,0,10,10"
                                 BindingContext="{x:Reference video}"
                                 Duration="{Binding Duration}"
                                 Position="{Binding Position}">
            <controls:PositionSlider.Triggers>
                <DataTrigger TargetType="controls:PositionSlider"
                             Binding="{Binding Status}"
                             Value="{x:Static controls:VideoStatus.NotReady}">
                    <Setter Property="IsEnabled"
                            Value="False" />
                </DataTrigger>
            </controls:PositionSlider.Triggers>
        </controls:PositionSlider>
    </Grid>
</ContentPage>

Vlastnost Position objektu Video je vázána na Position vlastnost PositionSlider, bez problémů s výkonem, protože Video.Position vlastnost je změněna metodou MauiVideoPlayer.UpdateStatus na každé platformě, která se nazývá pouze 10krát sekundu. Kromě toho dva Label objekty zobrazují Position hodnoty a TimeToEnd vlastnosti z objektu Video .

Vyčištění nativního zobrazení

Implementace obslužné rutiny DisconnectHandler každé platformy přepíše implementaci, která se používá k provádění nativního čištění zobrazení, jako je zrušení odběru událostí a odstraňování objektů. Toto přepsání však záměrně nevyvolává rozhraní .NET MAUI. Místo toho ho musíte vyvolat sami z vhodného umístění v životním cyklu vaší aplikace. To bude často v případě, že stránka obsahující Video ovládací prvek přejde pryč, což způsobí vyvolání události stránky Unloaded .

Obslužnou rutinu události pro událost stránky Unloaded je možné zaregistrovat v XAML:

<ContentPage ...
             xmlns:controls="clr-namespace:VideoDemos.Controls"
             Unloaded="OnContentPageUnloaded">
    <controls:Video x:Name="video"
                    ... />
</ContentPage>

Obslužná rutina události události Unloaded pak může vyvolat metodu DisconnectHandler ve své Handler instanci:

void OnContentPageUnloaded(object sender, EventArgs e)
{
    video.Handler?.DisconnectHandler();
}

Kromě čištění nativních prostředků zobrazení vyvolání metody obslužné rutiny DisconnectHandler také zajistí, aby se videa přestala přehrávat na zpětné navigaci v iOSu.