深入解读.NET MAUI音乐播放器项目(三):界面交互

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

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

深入解读.NET MAUI音乐播放器项目(三):界面交互_第1张图片
图片来自于App Store宣传图

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

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

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

页面

依赖包

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


  
  
  
  

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

页面设计

已注册的路由页面

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

以及导航或弹窗页面

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

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

界面设计风格设计如下:

深入解读.NET MAUI音乐播放器项目(三):界面交互_第2张图片

主页面

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

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

public partial class MainPage : Shell, ITransientDependency
{
    private readonly IocManager iocManager;

    public MainPage(IocManager iocManager)
	{
		InitializeComponent();
        this.iocManager = iocManager;
        this.Init();
        Loaded += MainPage_Loaded;
    }

    private async void MainPage_Loaded(object sender, EventArgs e)
    {
        var musicRelatedViewModel = iocManager.Resolve();
        await musicRelatedViewModel.InitAll();
    }
}

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


    


    


    
        
        
        
    


    

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

private void Init()
{
    var nowPlayingPage = iocManager.Resolve();
    var queuePage = iocManager.Resolve();
    var playlistPage = iocManager.Resolve();
    this.NowPlayingPageShellContent.Content = nowPlayingPage;
    this.QueuePageShellContent.Content = queuePage;
    this.PlaylistPageShellContent.Content = playlistPage;

    var musicPage = iocManager.Resolve();
    var albumPage = iocManager.Resolve();
    var artistPage = iocManager.Resolve();

    this.MusicPageShellContent.Content = musicPage;
    this.ArtistPageShellContent.Content = artistPage;
    this.AlbumPageShellContent.Content = albumPage;
}

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

public partial class App : Application
{
    private readonly AbpBootstrapper _abpBootstrapper;

    public App(AbpBootstrapper abpBootstrapper)
    {
        _abpBootstrapper = abpBootstrapper;
        InitializeComponent();
        _abpBootstrapper.Initialize();
        this.MainPage = abpBootstrapper.IocManager.Resolve(typeof(MainPage)) as MainPage;
    }
}

基础可视化元素类

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

ContentPageBase
ContentViewBase
PopupBase

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

深入解读.NET MAUI音乐播放器项目(三):界面交互_第3张图片

ContentPage和ContentViewBase包含曲目管理器IMusicInfoManager和播放控制服务IMusicControlService,类图如下
深入解读.NET MAUI音乐播放器项目(三):界面交互_第4张图片

导航

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

支持:

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

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

public async Task PushAsync(string pageName, object[] args = null)
 {
     var page = GetPageInstance(pageName, args);
     await mainPageNavigation.PushAsync(page);
 }
 
public async Task PushModalAsync(string pageName, object[] args = null)
{
    var page = GetPageInstance(pageName, args);
    await mainPageNavigation.PushModalAsync(page);
}

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

private Page GetPageInstance(string obj, object[] args, IList barItem = null)
{
    Page result = null;
    var namespacestr = "MatoMusic";
    Type pageType = Type.GetType(namespacestr + "." + obj, false);
    if (pageType != null)
    {
        try
        {
            var ctorInfo = pageType.GetConstructors()
                                  .Select(m => new
                                  {
                                      Method = m,
                                      Params = m.GetParameters(),
                                  }).Where(c => c.Params.Length == args.Length)
                                  .FirstOrDefault();
            if (ctorInfo==null)
            {
                throw new Exception("找不到对应的构造函数");
            }

            var argsDict = new Arguments();

            for (int i = 0; i < ctorInfo.Params.Length; i++)
            {
                var arg = ctorInfo.Params[i];
                argsDict.Add(arg.Name, args[i]);
            }

            var pageObj = iocManager.IocContainer.Resolve(pageType, argsDict) as Page;

            if (barItem != null && barItem.Count > 0)
            {
                foreach (var toolbarItem in barItem)
                {
                    pageObj.ToolbarItems.Add(toolbarItem);
                }
            }
            result = pageObj;
        }
        catch (Exception e)
        {
            Debug.WriteLine(e.Message);
        }
    }
    return result;
}

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

页面资源

NET MAUI 单一项目使资源文件可以存储在统一位置上(一般是Resources文件夹下),为跨平台方案使用。详情见官方文档
将在Fonts添加FontAwesome字体文件,以及Images中添加图标png文件
深入解读.NET MAUI音乐播放器项目(三):界面交互_第5张图片
MatoMusic.csproj文件中,对资源范围进行限定,此时的限定范围是Resources\Fonts\*Resources\Images\*


	
	

	
	

	
	
	

	
	

	
	

样式和主题

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

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

因此系统样式应该包含:

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

DarkTheme.xaml暗色主题配置

    #181818
    White
    #222326
    #DFD8F7
    Teal
    #A5A5A5

LightTheme.xaml亮色主题配置

    White
    #181818
    #DFD8F7
    #828386
    Teal
    #A5A5A5

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

Label全局样式



Button全局样式以及特定样式









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


    
        
            
            
            
        
    

字体

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

public static MauiApp CreateMauiApp()
{
	var builder = MauiApp.CreateBuilder();
	builder
		.UseMatoMusic()
		.UseMauiApp()
		.UseMauiCommunityToolkit()
		.ConfigureFonts(fonts =>
		{
			fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
			fonts.AddFont("FontAwesome.ttf", "FontAwesome");
		});
	return builder.Build();
}

在Xaml中使用:在带有文字属性的标签中,如

在本地计算机中安装好FontAwesome字体后,打开“字符映射表”工具,选择字体FontAwesome,点选后可以从下面的输入框中复制内容
深入解读.NET MAUI音乐播放器项目(三):界面交互_第6张图片
深入解读.NET MAUI音乐播放器项目(三):界面交互_第7张图片

本地化

使用Abp提供的本地化方案

在MatoMusic.Core项目的MatoMusicCoreModule.cs中

public class MatoMusicCoreModule : AbpModule
{
	public override void PreInitialize()
	{
		LocalizationConfigurer.Configure(Configuration.Localization);
	}
    ...
}
      

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

public static void Configure(ILocalizationConfiguration localizationConfiguration)
{
    localizationConfiguration.Sources.Add(
        new DictionaryBasedLocalizationSource(MatoMusicConsts.LocalizationSourceName,
            new XmlEmbeddedFileLocalizationDictionaryProvider(
                typeof(LocalizationConfigurer).GetAssembly(),
                "MatoMusic.Core.Localization.SourceFiles"
            )
        )
    );
}

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

protected virtual string L(string name)
{
    return LocalizationSource.GetString(name);
}

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

[ContentProperty("Text")]
public class TranslateExtension : DomainService, IMarkupExtension
{
    public TranslateExtension()
    {
        LocalizationSourceName = MatoMusicConsts.LocalizationSourceName;

    }
    public string Text { get; set; }

    public object ProvideValue(IServiceProvider serviceProvider)
    {
        Console.WriteLine(CultureInfo.CurrentUICulture);
        if (Text == null)
            return "";
        var translation = L(Text);
        return translation;
    }
}

在Xaml中使用:在带有文字属性的标签中,如

你可能感兴趣的:(.NetCore,.net,交互,ui,xamarin,c#)