Vytvoření vlastního ovládacího prvku pomocí obslužných rutin
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ů:
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:
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í:
- 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.
- Vytvořte všechny požadované další typy napříč platformami.
- Vytvořte
partial
třídu obslužné rutiny. Další informace naleznete v tématu Vytvoření obslužné rutiny. - 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í.
- 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ů.
- 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. - 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.Object
rovná . 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ána ve třídě .NET MAUI ViewHandler<TVirtualView,TPlatformView> 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ána ve třídě .NET MAUI ViewHandler<TVirtualView,TPlatformView> 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:
Třída VideoHandler
obsahující mappery má název VideoHandler.cs. Implementace platformy jsou v VideoHandler.Android.cs, VideoHandler.MaciOS.cs a VideoHandler.Windows.cs souborech. 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 ViewHandler<TVirtualView,TPlatformView> 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<TVirtualView,TPlatformView> 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í.
Důležité
Metoda DisconnectHandler je ve výchozím nastavení automaticky vyvolána rozhraním .NET MAUI, i když toto chování lze změnit. Další informace najdete v tématu Odpojení obslužné rutiny řízení.
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 VideoView
pomocí 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<TVirtualView,TPlatformView> 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 CoordinatorLayout
a podle potřeby ho umístit do rozložení. Zde je však přidán CoordinatorLayout
Android RelativeLayout
a VideoView
je přidán do RelativeLayout
. Parametry rozložení jsou nastaveny jak na RelativeLayout
VideoView
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 Prepared
Dispose
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 true
na , 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.AreTransportControlsEnabled
false
na , 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<TVirtualView,TPlatformView> 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 AVPlayerViewController
vlastnosti . 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.
Windows
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<TVirtualView,TPlatformView> 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 MediaOpened
Dispose
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 true
je , 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 VideoView
pomocí 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 UriVideoSource
se 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);
}
Windows
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 UriVideoSource
je 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 ResourceVideoSource
se 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 ResourceVideoSource
se 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);
}
Windows
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 ResourceVideoSource
je 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 FileVideoSource
se 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 FileVideoSource
se 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.
Windows
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 FileVideoSource
se 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 true
vlastnost , 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 true
vlastnost , 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 false
tato 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 Dispose
NSObject
na .
Windows
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
, Pause
a 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 , Play
Pause
a 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
, PauseRequested
a StopRequested
metody, které jsou prováděny v reakci na Video
ovládací prvek odesílání PlayRequested
, PauseRequested
a 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
, PauseRequested
a 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 Play
vyvolání , Pause
nebo 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 AVPlayerTimeControlStatus
je třeba získat přístup ke dvěma vlastnostem AVPlayer
TimeControlStatus
.Status
Vlastnost Status
pak lze nastavit na Video
objekt přetypováním na IVideoController
.
Windows
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.
Windows
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 Position
Video
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.
Windows
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 Slider TotalSeconds
TimeSpan
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 AutoPlay
false
, 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="▶️ Play"
HorizontalOptions="Center"
Clicked="OnPlayPauseButtonClicked">
<Button.Triggers>
<DataTrigger TargetType="Button"
Binding="{Binding Status}"
Value="{x:Static controls:VideoStatus.Playing}">
<Setter Property="Text"
Value="⏸ 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="⏹ 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 Video
AreTransportControlsEnabled
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:
Když se video přehrává, tlačítko přehrát se aktualizuje na tlačítko pozastavit:
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í , PositionSlider
který 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.
Odpojení obslužné rutiny řízení
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ů. Ve výchozím nastavení se obslužné rutiny automaticky odpojí od svých ovládacích prvků, pokud je to možné, například při navigaci dozadu v aplikaci.
V některých scénářích můžete chtít řídit, kdy se obslužná rutina odpojí od jeho ovládacího prvku, což lze dosáhnout pomocí HandlerProperties.DisconnectPolicy
připojené vlastnosti. Tato vlastnost vyžaduje HandlerDisconnectPolicy argument s výčtem definujícím následující hodnoty:
Automatic
, což označuje, že obslužná rutina bude odpojena automaticky. Toto je výchozí hodnotaHandlerProperties.DisconnectPolicy
připojené vlastnosti.Manual
, což označuje, že obslužná rutina bude muset být odpojena ručně vyvoláním DisconnectHandler() implementace.
Následující příklad ukazuje nastavení HandlerProperties.DisconnectPolicy
připojené vlastnosti:
<controls:Video x:Name="video"
HandlerProperties.DisconnectPolicy="Manual"
Source="video.mp4"
AutoPlay="False" />
Ekvivalentní kód jazyka C# je:
Video video = new Video
{
Source = "video.mp4",
AutoPlay = false
};
HandlerProperties.SetDisconnectPolicy(video, HandlerDisconnectPolicy.Manual);
Při nastavování HandlerProperties.DisconnectPolicy
připojené vlastnosti musíte Manual
vyvolat implementaci obslužné rutiny DisconnectHandler sami z vhodného umístění v životním cyklu vaší aplikace. Toho lze dosáhnout vyvoláním video.Handler?.DisconnectHandler();
.
Kromě toho existuje DisconnectHandlers metoda rozšíření, která od dané IViewmetody odpojí obslužné rutiny:
video.DisconnectHandlers();
Při odpojení se DisconnectHandlers metoda rozšíří dolů řídicí strom, dokud se nedokončí nebo dorazí do ovládacího prvku, který nastavil ruční zásadu.