经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » ASP.net » 查看文章
深入解读.NET MAUI音乐播放器项目(三):界面交互
来源:cnblogs  作者:林晓lx  时间:2023/2/28 8:51:59  对本文有异议

UI设计的本质是对于产品的理解在界面中多种形式的映射,当需求和定位不同时,对相同的功能表达出了不同的界面和交互方式。

作为播放器,界面可以是千差万别的。《番茄播放器》的iOS平台上我开发了传统版本,和基于手势播放的版本。

在这里插入图片描述
图片来自于App Store宣传图

它们界面不同,但用的同一个播放内核。

作为播放内核项目,在MatoMusic.Core的工作已经结束。本系列博文重点还是在播放器思路的解读,关于MAUI动画交互,我打算有时间另外写博客(这里给自己挖个坑)。 本项目中朴实无华的播放器界面部分,我想仅作为辅佐播放内核的示例,对于页面和控件的Xaml部分不会展开描述。

在解决方案管理器中,我们新建MatoMusic项目,作为UI部分。

页面

依赖包

在MatoMusic.csproj中添加对AbpAbp.AutoMapperAbp.Castle.Log4NetCommunityToolkit.Maui的包依赖

  1. <ItemGroup>
  2. <PackageReference Include="Abp" Version="7.4.0" />
  3. <PackageReference Include="Abp.AutoMapper" Version="7.4.0" />
  4. <PackageReference Include="Abp.Castle.Log4Net" Version="7.4.0" />
  5. <PackageReference Include="CommunityToolkit.Maui" Version="2.0.0" />
  6. </ItemGroup>

CommunityToolkit.Maui.Views.Popup为系统提供弹窗页面支持

页面设计

已注册的路由页面

  • NowPlayingPage - 正在播放页面
  • QueuePage - 播放队列页面
  • MusicPage - 歌曲页面
  • ArtistPage - 艺术家页面
  • AlbumPage - 专辑页面
  • PlaylistPage - 歌单页面

以及导航或弹窗页面

  • MusicCollectionPage - 歌曲集合详情页面
  • PlaylistEntryPage - 歌单详情页面
  • PlaylistFunctionPage - 歌单功能列表页面
  • PlaylistChoosePage - 歌单选择页面

路由页面可以从侧滑菜单栏或功能列表中通过指定的Uri跳转

界面设计风格设计如下:

在这里插入图片描述

主页面

.NET MAUI Shell 通过提供大多数应用所需的基本功能来降低应用开发的复杂性,应用视觉对象层次结构导航,详情见官方文档

建立一个Shell页面MainPage.cs作为初始界面:
在页面Load完成后调用IMusicRelatedViewModel.InitAll()方法

  1. public partial class MainPage : Shell, ITransientDependency
  2. {
  3. private readonly IocManager iocManager;
  4. public MainPage(IocManager iocManager)
  5. {
  6. InitializeComponent();
  7. this.iocManager = iocManager;
  8. this.Init();
  9. Loaded += MainPage_Loaded;
  10. }
  11. private async void MainPage_Loaded(object sender, EventArgs e)
  12. {
  13. var musicRelatedViewModel = iocManager.Resolve<MusicRelatedService>();
  14. await musicRelatedViewModel.InitAll();
  15. }
  16. }

在Xaml中定义各页面的层次结构,隐式注册的路由页面:

  1. <FlyoutItem Route="NowPlayingPage" Title="正在播放" Icon="tab_home.png">
  2. <ShellContent x:Name="NowPlayingPageShellContent"/>
  3. </FlyoutItem>
  4. <FlyoutItem Route="QueuePage" Title="播放队列" Icon="tab_favorites.png">
  5. <ShellContent x:Name="QueuePageShellContent"/>
  6. </FlyoutItem>
  7. <FlyoutItem Route="LibraryMainPage" Title="库" Icon="tab_map.png" >
  8. <Tab>
  9. <ShellContent Title="歌曲" Icon="headphone.png" x:Name="MusicPageShellContent"/>
  10. <ShellContent Title="艺术家" Icon="microphone2.png" x:Name="ArtistPageShellContent"/>
  11. <ShellContent Title="专辑" Icon="cd2.png" x:Name="AlbumPageShellContent"/>
  12. </Tab>
  13. </FlyoutItem>
  14. <FlyoutItem Route="PlaylistPage" Title="歌单" Icon="tab_map.png">
  15. <ShellContent x:Name="PlaylistPageShellContent"/>
  16. </FlyoutItem>

后端代码中为各ShellContent指定页面对象

  1. private void Init()
  2. {
  3. var nowPlayingPage = iocManager.Resolve<NowPlayingPage>();
  4. var queuePage = iocManager.Resolve<QueuePage>();
  5. var playlistPage = iocManager.Resolve<PlaylistPage>();
  6. this.NowPlayingPageShellContent.Content = nowPlayingPage;
  7. this.QueuePageShellContent.Content = queuePage;
  8. this.PlaylistPageShellContent.Content = playlistPage;
  9. var musicPage = iocManager.Resolve<MusicPage>();
  10. var albumPage = iocManager.Resolve<AlbumPage>();
  11. var artistPage = iocManager.Resolve<ArtistPage>();
  12. this.MusicPageShellContent.Content = musicPage;
  13. this.ArtistPageShellContent.Content = artistPage;
  14. this.AlbumPageShellContent.Content = albumPage;
  15. }

在App.xaml.cs中配置初始页面

  1. public partial class App : Application
  2. {
  3. private readonly AbpBootstrapper _abpBootstrapper;
  4. public App(AbpBootstrapper abpBootstrapper)
  5. {
  6. _abpBootstrapper = abpBootstrapper;
  7. InitializeComponent();
  8. _abpBootstrapper.Initialize();
  9. this.MainPage = abpBootstrapper.IocManager.Resolve(typeof(MainPage)) as MainPage;
  10. }
  11. }

基础可视化元素类

其中ContentPageContentViewPopup分别继承于以下三个类别

ContentPageBase
ContentViewBase
PopupBase

他们包含Abp提供的本地化,对象映射,设置等服务,类图如下

在这里插入图片描述

ContentPage和ContentViewBase包含曲目管理器IMusicInfoManager和播放控制服务IMusicControlService,类图如下
在这里插入图片描述

导航

NavigationService,封装了初始页面的INavigation对象和导航方法

支持:

  • 路由方式的导航 - Shell 视觉层次结构中隐式注册的路由。
  • 页面导航 - 模式导航页面可以从应用的任何位置推送到堆叠导航。

PushAsync或PushModalAsync可以按文件名的页面导航

  1. public async Task PushAsync(string pageName, object[] args = null)
  2. {
  3. var page = GetPageInstance(pageName, args);
  4. await mainPageNavigation.PushAsync(page);
  5. }
  6. public async Task PushModalAsync(string pageName, object[] args = null)
  7. {
  8. var page = GetPageInstance(pageName, args);
  9. await mainPageNavigation.PushModalAsync(page);
  10. }

GetPageInstance通过反射的方式创建页面对象
传入对象名称,参数和工具栏项目对象,返回页面对象

  1. private Page GetPageInstance(string obj, object[] args, IList<ToolbarItem> barItem = null)
  2. {
  3. Page result = null;
  4. var namespacestr = "MatoMusic";
  5. Type pageType = Type.GetType(namespacestr + "." + obj, false);
  6. if (pageType != null)
  7. {
  8. try
  9. {
  10. var ctorInfo = pageType.GetConstructors()
  11. .Select(m => new
  12. {
  13. Method = m,
  14. Params = m.GetParameters(),
  15. }).Where(c => c.Params.Length == args.Length)
  16. .FirstOrDefault();
  17. if (ctorInfo==null)
  18. {
  19. throw new Exception("找不到对应的构造函数");
  20. }
  21. var argsDict = new Arguments();
  22. for (int i = 0; i < ctorInfo.Params.Length; i++)
  23. {
  24. var arg = ctorInfo.Params[i];
  25. argsDict.Add(arg.Name, args[i]);
  26. }
  27. var pageObj = iocManager.IocContainer.Resolve(pageType, argsDict) as Page;
  28. if (barItem != null && barItem.Count > 0)
  29. {
  30. foreach (var toolbarItem in barItem)
  31. {
  32. pageObj.ToolbarItems.Add(toolbarItem);
  33. }
  34. }
  35. result = pageObj;
  36. }
  37. catch (Exception e)
  38. {
  39. Debug.WriteLine(e.Message);
  40. }
  41. }
  42. return result;
  43. }

其中,弹窗的打开和关闭由扩展类CommunityToolkit.Maui.Views.PopupExtensions提供方法

页面资源

NET MAUI 单一项目使资源文件可以存储在统一位置上(一般是Resources文件夹下),为跨平台方案使用。详情见官方文档
将在Fonts添加FontAwesome字体文件,以及Images中添加图标png文件
在这里插入图片描述
MatoMusic.csproj文件中,对资源范围进行限定,此时的限定范围是Resources\Fonts\*Resources\Images\*

  1. <ItemGroup>
  2. <!-- App Icon -->
  3. <MauiIcon Include="Resources\AppIcon\appicon.svg" ForegroundFile="Resources\AppIcon\appiconfg.svg" Color="#512BD4" />
  4. <!-- Splash Screen -->
  5. <MauiSplashScreen Include="Resources\Splash\splash.svg" Color="#512BD4" BaseSize="128,128" />
  6. <!-- Images -->
  7. <MauiImage Include="Resources\Images\*" />
  8. <MauiImage Update="Resources\Images\dotnet_bot.svg" BaseSize="168,208" />
  9. <!-- Custom Fonts -->
  10. <MauiFont Include="Resources\Fonts\*" />
  11. <!-- Raw Assets (also remove the "Resources\Raw" prefix) -->
  12. <MauiAsset Include="Resources\Raw\**" LogicalName="%(RecursiveDir)%(Filename)%(Extension)" />
  13. </ItemGroup>

样式和主题

在移动端应用配色设计上,不同的应用应该有其独特的设计风格,但因遵循配色原理。

基本配色要求有:应该保持主色统一,使主题鲜明突出;前景、背景色反差强烈使内容更容易阅读;前景、背景有相应辅助色使界面灵动不古板。

因此系统样式应该包含:

  • PhoneForegroundBrush - 前景色
  • PhoneContrastForegroundBrush - 辅前景色
  • PhoneBackgroundBrush - 背景色
  • PhoneContrastBackgroundBrush - 辅背景色
  • PhoneAccentBrush - 主色(亮色)
  • PhoneChromeBrush - 暗色

DarkTheme.xaml暗色主题配置

  1. <Color x:Key="PhoneBackgroundBrush">#181818</Color>
  2. <Color x:Key="PhoneForegroundBrush">White</Color>
  3. <Color x:Key="PhoneContrastBackgroundBrush">#222326</Color>
  4. <Color x:Key="PhoneContrastForegroundBrush">#DFD8F7</Color>
  5. <Color x:Key="PhoneAccentBrush">Teal</Color>
  6. <Color x:Key="PhoneChromeBrush">#A5A5A5</Color>

LightTheme.xaml亮色主题配置

  1. <Color x:Key="PhoneBackgroundBrush">White</Color>
  2. <Color x:Key="PhoneForegroundBrush">#181818</Color>
  3. <Color x:Key="PhoneContrastBackgroundBrush">#DFD8F7</Color>
  4. <Color x:Key="PhoneContrastForegroundBrush">#828386</Color>
  5. <Color x:Key="PhoneAccentBrush">Teal</Color>
  6. <Color x:Key="PhoneChromeBrush">#A5A5A5</Color>

CommonResourceDictionary.xaml中定义通用的控件样式,如Label和Button控件,部分的定义如下

Label全局样式

  1. <Style TargetType="Label">
  2. <Setter Property="TextColor" Value="{DynamicResource PhoneForegroundBrush}" />
  3. <Setter Property="FontFamily" Value="OpenSansRegular" />
  4. </Style>

Button全局样式以及特定样式

  1. <Style TargetType="Button">
  2. <Setter Property="CornerRadius" Value="8"/>
  3. <Setter Property="TextColor" Value="{DynamicResource PhoneContrastForegroundBrush}" />
  4. <Setter Property="FontFamily" Value="OpenSansRegular" />
  5. <Setter Property="BackgroundColor" Value="{DynamicResource PhoneContrastBackgroundBrush}" />
  6. <Setter Property="Padding" Value="14,10" />
  7. <Setter Property="Margin" Value="5,0" />
  8. </Style>
  9. <Style TargetType="Button" x:Key="PrimaryButton">
  10. <Setter Property="CornerRadius" Value="8"/>
  11. <Setter Property="BackgroundColor" Value="Transparent"/>
  12. <Setter Property="TextColor" Value="{DynamicResource PhoneContrastForegroundBrush}" />
  13. <Setter Property="FontFamily" Value="OpenSansRegular" />
  14. <Setter Property="BackgroundColor" Value="{DynamicResource PhoneAccentBrush}" />
  15. <Setter Property="Padding" Value="14,10" />
  16. <Setter Property="Margin" Value="5,0" />
  17. </Style>
  18. <Style TargetType="Button" x:Key="TextButton">
  19. <Setter Property="BackgroundColor" Value="Transparent"/>
  20. <Setter Property="TextColor" Value="{DynamicResource PhoneContrastForegroundBrush}" />
  21. <Setter Property="FontFamily" Value="OpenSansRegular" />
  22. <Setter Property="BorderWidth" Value="0"/>
  23. <Setter Property="Padding" Value="14,10" />
  24. <Setter Property="Margin" Value="5,0" />
  25. </Style>
  26. <Style TargetType="Button" x:Key="PrimaryButtonOutline">
  27. <Setter Property="CornerRadius" Value="8"/>
  28. <Setter Property="BackgroundColor" Value="Transparent"/>
  29. <Setter Property="TextColor" Value="{DynamicResource PhoneContrastForegroundBrush}" />
  30. <Setter Property="FontFamily" Value="OpenSansRegular" />
  31. <Setter Property="BorderWidth" Value="1"/>
  32. <Setter Property="BorderColor" Value="{DynamicResource PhoneAccentBrush}"/>
  33. <Setter Property="Padding" Value="14,10" />
  34. <Setter Property="Margin" Value="5,0" />
  35. </Style>

App.xaml中将主题和通用样式囊括到资源字典中

  1. <Application.Resources>
  2. <ResourceDictionary>
  3. <ResourceDictionary.MergedDictionaries>
  4. <style:DarkTheme />
  5. <!--<style:LightTheme />-->
  6. <style:CommonResourceDictionary />
  7. </ResourceDictionary.MergedDictionaries>
  8. </ResourceDictionary>
  9. </Application.Resources>

字体

配置
在MauiProgram.cs中,CreateMauiApp里将FontAwesome字体加入配置

  1. public static MauiApp CreateMauiApp()
  2. {
  3. var builder = MauiApp.CreateBuilder();
  4. builder
  5. .UseMatoMusic<MatoMusicModule>()
  6. .UseMauiApp<App>()
  7. .UseMauiCommunityToolkit()
  8. .ConfigureFonts(fonts =>
  9. {
  10. fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
  11. fonts.AddFont("FontAwesome.ttf", "FontAwesome");
  12. });
  13. return builder.Build();
  14. }

在Xaml中使用:在带有文字属性的标签中,如<Button><Label>,设置FontFamily属性为FontAwesome,设置Text属性为FontAwesome字符内容,此值可使用“字符映射表”工具查找。

  1. <Button Text="?" FontFamily="FontAwesome"></Button>

在本地计算机中安装好FontAwesome字体后,打开“字符映射表”工具,选择字体FontAwesome,点选后可以从下面的输入框中复制内容
在这里插入图片描述
在这里插入图片描述

本地化

使用Abp提供的本地化方案

在MatoMusic.Core项目的MatoMusicCoreModule.cs中

  1. public class MatoMusicCoreModule : AbpModule
  2. {
  3. public override void PreInitialize()
  4. {
  5. LocalizationConfigurer.Configure(Configuration.Localization);
  6. }
  7. ...
  8. }

Localization/MatoMusicLocalization.cs中,将提供基于Xml的本地化的语言字典配置,从MatoMusic.Core.Localization.SourceFiles资源中访问字典:

  1. public static void Configure(ILocalizationConfiguration localizationConfiguration)
  2. {
  3. localizationConfiguration.Sources.Add(
  4. new DictionaryBasedLocalizationSource(MatoMusicConsts.LocalizationSourceName,
  5. new XmlEmbeddedFileLocalizationDictionaryProvider(
  6. typeof(LocalizationConfigurer).GetAssembly(),
  7. "MatoMusic.Core.Localization.SourceFiles"
  8. )
  9. )
  10. );
  11. }

在这些文件的编译模式应为嵌入的资源
在这里插入图片描述
基础可视化元素类中提供L方法,返回本地化字符串

  1. protected virtual string L(string name)
  2. {
  3. return LocalizationSource.GetString(name);
  4. }

TranslateExtension实现IMarkupExtension,MarkupLanguage的本质,是实例化一个对象。Xaml编译器,会调用标记扩展对象的ProvideValue方法,并将返回值赋值给使用了标记扩展的属性,ProvideValue中调用L方法完成翻译

  1. [ContentProperty("Text")]
  2. public class TranslateExtension : DomainService, IMarkupExtension
  3. {
  4. public TranslateExtension()
  5. {
  6. LocalizationSourceName = MatoMusicConsts.LocalizationSourceName;
  7. }
  8. public string Text { get; set; }
  9. public object ProvideValue(IServiceProvider serviceProvider)
  10. {
  11. Console.WriteLine(CultureInfo.CurrentUICulture);
  12. if (Text == null)
  13. return "";
  14. var translation = L(Text);
  15. return translation;
  16. }
  17. }

在Xaml中使用:在带有文字属性的标签中,如<Button><Label>,Text属性的值将转换为本地化字符串值。

ViewModel

Model-View-ViewModel (MVVM) 设计模式是在称为视图的 Xaml 用户界面和基础数据(称为模型)之间的一个软件层,称之为视图模型,即ViewModel,

界面视图和ViewModel通过Xaml中定义的数据绑定进行连接。 在视图类的构造函数中,我们以注入的方式将ViewModel导入视图,并赋值给BindingContext属性,例如在NowPlayingPage.cs中:

  1. public NowPlayingPage(NowPlayingPageViewModel nowPlayingPageViewModel)
  2. {
  3. InitializeComponent();
  4. this.BindingContext = nowPlayingPageViewModel;
  5. ...
  6. }

音乐相关服务类

前一章介绍了播放核心的两个类曲目管理器IMusicInfoManager和播放控制服务IMusicControlService,界面交互对象中对这两个类进行了应用。

MusicRelatedService是播放控制服务的一层封装,它基于ViewModelBase。

抽象的来说,音乐相关服MusicRelatedService包含一系列可绑定的属性,自动维护属性值,并在设置属性值时调用放控制服务完成业务变更操作。数据作为界面交互的支撑。

主要属性:

  • NextMusic - 下一首曲目
  • PreviewMusic - 上一首曲目

NextMusic和PreviewMusic用于绑定首页的上一曲、下一曲专辑封面。

  • CurrentMusic - 当前曲目:正在播放的曲目,所有的音乐相关操作的对象都是当前曲目CurrentMusic。

CurrentMusic可在界面提供双向绑定支持,当其值变更时,代表切换播放歌曲。

  1. 调用IMmusicControlService.InitPlayer,设置播放器曲目
  2. 更新当前曲目的长度
  3. 更新上一首、下一首曲目
  4. 更新BreakPointMusicIndex值,并用SettingManager持久化当前曲目的角标编号

代码实现如下:

  1. if (e.PropertyName == nameof(CurrentMusic))
  2. {
  3. if (!Canplay || IsInited == false)
  4. {
  5. return;
  6. }
  7. await musicControlService.InitPlayer(CurrentMusic);
  8. DoUpdate();
  9. InitPreviewAndNextMusic();
  10. Duration = GetPlatformSpecificTime(musicControlService.Duration());
  11. SettingManager.ChangeSettingForApplication(CommonSettingNames.BreakPointMusicIndex, Musics.IndexOf(CurrentMusic).ToString());
  12. }
  • Musics - 当前播放队列:可供播放的有序曲目集合,是自然播放、上一曲、下一曲、随机播放的范围。

  • Canplay - 表明当前曲目是否可供播放

实现如下:

  1. public bool Canplay => this.CurrentMusic != null;
  • CanplayAll - 指示是否可以播放全部曲目,当当前播放队列为空时,界面将显示向导

实现如下:

  1. public bool CanplayAll => Musics.Count > 0;
  • IsPlaying 表明是否正在播放

它的值变更由IMusicControlService.OnPlayStatusChanged事件触发,以实现自动维护属性值:

  1. musicControlService.OnPlayStatusChanged+=MusicControlService_OnPlayStatusChanged;
  1. private void MusicControlService_OnPlayStatusChanged(object sender, bool e)
  2. {
  3. this.IsPlaying = e;
  4. }
  • Duration - 指示当前曲目时长
  • CurrentTime - 指示当前曲目播放进度

这两个属性由一个自动定时器,每隔一段时间自动触发DoUpdate方法,以实现自动维护属性值:

  1. public bool DoUpdate()
  2. {
  3. this.CurrentTime = GetPlatformSpecificTime(musicControlService.CurrentTime());
  4. this.Duration = GetPlatformSpecificTime(musicControlService.Duration());
  5. return true;
  6. }
  • IsShuffle - 指示随机播放模式,是否为随机播放
  • IsRepeatOne - 指示单曲循环模式,是否为单曲循环

这两个属性由SettingManager持久化其值。

  • IsInited - 指示是否完成初始化服务

主要方法:

  • InitCurrentMusic - 初始化当前曲目CurrentMusic

根据当前曲目的角标编号BreakPointMusicIndex值,从曲目库中获取当前曲目对象,并赋值给CurrentMusic。

  • InitAll - 初始化服务,用于系统启动后的一次性调用

调用IMusicControlService.RebuildMusicInfos从播放列队中读取音频列表,完成后触发OnBuildMusicInfosFinished事件,触发InitCurrentMusic。

音乐相关ViewModel类

MusicRelatedViewModel是一个抽象类,基于ViewModelBase,包含MusicRelatedService,以及曲目管理器IMusicInfoManager和播放控制服务IMusicControlService对象,其子类可直接用于界面UI元素的绑定。

MusicRelatedViewModel的子类中,MusicRelatedService对象不需要在构造函数中注入,它将在访问器中初始化。

  1. public MusicRelatedService MusicRelatedService
  2. {
  3. get
  4. {
  5. if (_musicRelatedService==null)
  6. {
  7. _musicRelatedService = IocManager.Instance.Resolve<MusicRelatedService>();
  8. _musicRelatedService.PropertyChanged += this.Delegate_PropertyChanged;
  9. }
  10. return _musicRelatedService;
  11. }
  12. }

并且在MusicRelatedViewModel的子类中,实现了对MusicRelatedService对象属性变更的事件冒泡:

  1. private void Delegate_PropertyChanged(object sender, PropertyChangedEventArgs e)
  2. {
  3. this.RaisePropertyChanged(e.PropertyName);
  4. }

主要属性:

  • NextMusic - 下一首曲目

  • PreviewMusic - 上一首曲目

  • CurrentMusic - 当前曲目:正在播放的曲目,所有的音乐相关操作的对象都是当前曲目CurrentMusic。

  • Musics - 当前播放队列:可供播放的有序曲目集合,是自然播放、上一曲、下一曲、随机播放的范围。

  • Canplay - 表明当前曲目是否可供播放

  • CanplayAll - 指示是否可以播放全部曲目,当当前播放队列为空时,界面将显示向导

  • IsPlaying 表明是否正在播放

  • Duration - 指示当前曲目时长

  • CurrentTime - 指示当前曲目播放进度

  • IsShuffle - 指示随机播放模式,是否为随机播放

  • IsRepeatOne - 指示单曲循环模式,是否为单曲循环

  • PlayCommand - 播放/暂停命令

  • PreCommand - 上一曲命令

  • NextCommand - 下一曲命令

  • ShuffleCommand - 切换随机模式命令

  • RepeatOneCommand - 切换单曲循环命令

  • FavouriteCommand - 设置/取消设置“我最喜爱”

数据绑定

在NowPlayingPage中,对当前播放的曲目,以及上一首、下一首的曲目信息进行显示。
对于当前曲目的长度和进度,进行显示和控制,对播放的曲目进行控制等内容:

  1. <Grid Grid.Column="1">
  2. <Image HorizontalOptions="Fill"
  3. x:Name="PreAlbumArt"
  4. HeightRequest="{Binding Source={x:Reference Name=RefBox}, Path=Height}"
  5. WidthRequest="{Binding Source={x:Reference Name=RefBox}, Path=Width}"
  6. VerticalOptions="Fill"
  7. TranslationX="-320"
  8. Source="{Binding PreviewMusic.AlbumArt,Converter={StaticResource AlbumArtConverter}}">
  9. </Image>
  10. <Image
  11. HeightRequest="{Binding Source={x:Reference Name=RefBox}, Path=Height}"
  12. WidthRequest="{Binding Source={x:Reference Name=RefBox}, Path=Width}"
  13. HorizontalOptions="Fill"
  14. VerticalOptions="Fill"
  15. Source="{Binding CurrentMusic.AlbumArt,Converter={StaticResource AlbumArtConverter}}">
  16. <Image.GestureRecognizers>
  17. <TapGestureRecognizer Command="{Binding SwitchPannelCommand}"></TapGestureRecognizer>
  18. </Image.GestureRecognizers>
  19. </Image>
  20. <Image
  21. x:Name="NextAlbumArt"
  22. HeightRequest="{Binding Source={x:Reference Name=RefBox}, Path=Height}"
  23. WidthRequest="{Binding Source={x:Reference Name=RefBox}, Path=Width}"
  24. HorizontalOptions="Fill"
  25. VerticalOptions="Fill"
  26. TranslationX="320"
  27. Source="{Binding NextMusic.AlbumArt,Converter={StaticResource AlbumArtConverter}}">
  28. </Image>
  29. </Grid>

对曲目名称,艺术家的绑定:

  1. <StackLayout Grid.Column="1" HorizontalOptions="Center">
  2. <Label Text="{Binding CurrentMusic.Title}"
  3. HorizontalOptions="FillAndExpand"
  4. HorizontalTextAlignment="Center"
  5. FontSize="{StaticResource BodyFontSize}"
  6. TextColor="White" />
  7. <Label Margin="0,-5,0,0"
  8. Text="{Binding CurrentMusic.Artist}"
  9. LineBreakMode="TailTruncation"
  10. HorizontalOptions="FillAndExpand"
  11. HorizontalTextAlignment="Center"
  12. FontSize="{StaticResource BodyFontSize}"
  13. TextColor="{DynamicResource PhoneChromeBrush}" />
  14. </StackLayout>

界面效果如下:
在这里插入图片描述
小窗播放控件MusicMiniView也对曲目信息进行了相似的绑定
在这里插入图片描述
进度控制区域代码:

  1. <!--进度控制区域-->
  2. <Grid Grid.Row="2"
  3. x:Name="ProgressControlLayout"
  4. BindingContext="{Binding}">
  5. <StackLayout Margin="0,0,0,0" Orientation="Horizontal">
  6. <Label Text="{Binding CurrentTime,Converter={StaticResource SecondsToTimeSpanConverter}}"
  7. TextColor="{DynamicResource PhoneChromeBrush}"
  8. FontSize="{StaticResource TinyFontSize}"
  9. HorizontalOptions="StartAndExpand" />
  10. <Label Text="{Binding Duration,Converter={StaticResource SecondsToTimeSpanConverter}}"
  11. TextColor="{DynamicResource PhoneChromeBrush}"
  12. FontSize="{StaticResource TinyFontSize}"
  13. HorizontalOptions="End" />
  14. </StackLayout>
  15. <Slider
  16. Maximum="{Binding Duration,Converter={StaticResource SliderMaxValueConverter}}"
  17. Minimum="0.0"
  18. MinimumTrackColor="{DynamicResource PhoneAccentBrush}"
  19. IsEnabled="{Binding Canplay}"
  20. ValueChanged="OnValueChanged"
  21. Value="{Binding CurrentTime,Mode=TwoWay} ">
  22. </Slider>
  23. </Grid>

播放控制区域代码:

  1. <!--播放控制按钮-->
  2. <Grid
  3. Grid.Row="3"
  4. BindingContext="{Binding}"
  5. x:Name="PlayControlLayout">
  6. <Grid.ColumnDefinitions>
  7. <ColumnDefinition Width="1*"></ColumnDefinition>
  8. <ColumnDefinition Width="Auto"></ColumnDefinition>
  9. <ColumnDefinition Width="1*"></ColumnDefinition>
  10. </Grid.ColumnDefinitions>
  11. <Button VerticalOptions="Center"
  12. HorizontalOptions="StartAndExpand"
  13. Grid.Column="0"
  14. Command="{Binding ShuffleCommand}"
  15. FontFamily="FontAwesome"
  16. FontSize="{StaticResource BodyFontSize}"
  17. Text="{Binding IsShuffle,Converter={StaticResource Bool2StringConverter},ConverterParameter=?|?}"/>
  18. <Grid Grid.Column="1" HorizontalOptions="Center" WidthRequest="216">
  19. <Grid.ColumnDefinitions>
  20. <ColumnDefinition Width="1*"></ColumnDefinition>
  21. <ColumnDefinition Width="2*"></ColumnDefinition>
  22. <ColumnDefinition Width="1*"></ColumnDefinition>
  23. </Grid.ColumnDefinitions>
  24. <Button
  25. VerticalOptions="Center"
  26. Grid.Column="0"
  27. Command="{Binding PreCommand}"
  28. FontFamily="FontAwesome"
  29. FontSize="{StaticResource BodyFontSize}"
  30. Text="?"
  31. />
  32. <Button
  33. VerticalOptions="Center"
  34. Grid.Column="1"
  35. Command="{Binding PlayCommand}"
  36. Style="{StaticResource PrimaryButton}"
  37. FontFamily="FontAwesome"
  38. FontSize="{StaticResource StandOutBodyFontSize}"
  39. Text="{Binding IsPlaying,Converter={StaticResource Bool2StringConverter},ConverterParameter=?|?} "/>
  40. <Button
  41. VerticalOptions="Center"
  42. Grid.Column="2"
  43. Command="{Binding NextCommand}"
  44. FontFamily="FontAwesome"
  45. FontSize="{StaticResource BodyFontSize}"
  46. Text="?"/>
  47. </Grid>
  48. <Button VerticalOptions="Center"
  49. HorizontalOptions="EndAndExpand"
  50. Grid.Column="2"
  51. Command="{Binding RepeatOneCommand}"
  52. FontFamily="FontAwesome"
  53. FontSize="{StaticResource BodyFontSize}"
  54. Text="{Binding IsRepeatOne,Converter={StaticResource Bool2StringConverter},ConverterParameter=?|?}" />
  55. </Grid>

列表分组显示

下列三个页面使用ListView控件呈现曲目,专辑,艺术家的可滚动垂直列表

  • MusicPage - 歌曲页面
  • ArtistPage - 艺术家页面
  • AlbumPage - 专辑页面

通过将 设置 ListView.GroupHeaderTemplateDataTemplate来自定义每个组标头的外观

在MusicGroupHeaderView.xaml中,定义分组标头的外观,由一个标题和亮色方形背景组成

  1. <ContentView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
  2. xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
  3. x:Class="MatoMusic.MusicGroupHeaderView">
  4. <Grid>
  5. <Grid.ColumnDefinitions>
  6. <ColumnDefinition Width="Auto">
  7. </ColumnDefinition>
  8. <ColumnDefinition>
  9. </ColumnDefinition>
  10. </Grid.ColumnDefinitions>
  11. <BoxView Color="{DynamicResource PhoneAccentBrush}"
  12. Margin="0,5"
  13. WidthRequest="54">
  14. </BoxView>
  15. <Label VerticalTextAlignment="Center"
  16. HorizontalTextAlignment="Center"
  17. Text="{Binding Title}"
  18. FontAttributes="Bold"
  19. TextColor="{DynamicResource PhoneForegroundBrush}"
  20. FontSize="{StaticResource BodyFontSize}">
  21. </Label>
  22. </Grid>
  23. </ContentView>

在页面控件中,IsGroupingEnabled设置为true,并指定GroupHeaderTemplate属性为MusicGroupHeaderView

  1. <ListView
  2. IsGroupingEnabled="true"
  3. <ListView.GroupHeaderTemplate>
  4. <DataTemplate>
  5. <ViewCell>
  6. <mato:MusicGroupHeaderView></mato:MusicGroupHeaderView>
  7. </ViewCell>
  8. </DataTemplate>
  9. </ListView.GroupHeaderTemplate>
  10. <ListView.ItemTemplate>

界面效果如下
在这里插入图片描述

项目地址

GitHub:MatoMusic

原文链接:https://www.cnblogs.com/jevonsflash/p/17160540.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号