乘風破浪,.Net Core遇見MAUI(.NET Multi-platform App UI),進擊現代化跨設備應用框架


什么是MAUI

https://github.com/dotnet/maui

.NET Multi-platform App UI (MAUI) 的前身是Xamarin.Forms(適用於Android,iOS和UWP的跨平台移動優先框架),.NET MAUI是Xamarin.Forms的演進。我們擁有7年的為客戶提供技術支持的經驗,服務對象從獨立開發人員到一些全球性的大公司,我們正在改善產品的核心功能,加快UI渲染,投資研發一致的系統設計模式,並從移動端擴展到桌面端。

image

image

image

與WinUI的區別

MAUI是一個跨iOS、Android、Windows、MacOs多設備多生態的應用框架,而WinUI僅限於Windows。

面向用戶

.NET MAUI is for developers who want to:

  • Write cross-platform apps in XAML and C#, from a single shared code-base in Visual Studio.
  • Share UI layout and design across platforms.
  • Share code, test, and business logic across platforms.

工作原理

.NET MAUI將Android、iOS、macOS和WindowsAPI統一為單個API,允許在任何地方開發人員處進行筆寫體驗,同時提供對每個本地平台各個方面的深度訪問。

.NET 6為創建應用提供了一系列特定於平台的框架:.Android的.NET、iOS的.NET、macOS的.NET和WindowsUI(WinUI)庫。這些框架都可以訪問相同的.NET 6基礎類庫(BCL)。此庫將基礎平台的詳細信息從您的代碼中摘要出來。BCL取決於.NET運行時間為您的代碼提供執行環境。對於安卓、iOS和macOS,環境由單聲道實施,這是.NET運行時間的實現。在Windows上,WinRT執行相同的角色,但窗口平台已優化除外。

雖然BCL使在不同平台上運行的應用能夠共享共同的商業邏輯,但不同的平台具有定義應用用戶界面的不同方式,它們為指定用戶界面元素的溝通和互操作方式提供了不同的模型。您可以使用適當的平台特定框架(.Android的NET、iOS的.NET、macOS的.NET或WinUI)分別為每個平台制作UI,但此方法需要您為每個設備系列維護代碼基礎。

.NET MAUI為為移動和桌面應用構建UI提供了單一框架。下圖顯示了.NETMAUI應用程序的架構的高層視圖:

image

在.NET MAUI應用中,您編寫的代碼主要與NET MAUI API交互。.NET MAUI然后直接消耗原生平台API。此外,如果需要,應用代碼可以直接執行平台API。

.NET MAUI應用可以寫在PC或Mac上,並編譯為原生應用包:

  • 使用.NET MAUI構建的Android應用從C#編譯為中間語言(IL),然后在應用啟動時及時(JIT)編譯到本地裝配中。
  • 使用.NET MAUI構建的iOS應用完全提前從C#編譯為原生ARM裝配代碼。
  • 使用.NET MAUI構建的macOS應用使用MacCatalyst,這是蘋果的解決方案,將使用UIKit構建的iOS應用引入桌面,並根據需要通過額外的AppKit和平台API來增強它。
  • 使用.NET MAUI構建的Windows應用使用WindowsUI庫(WinUI)3創建可以針對Windows桌面和通用視窗平台(UWP)的原生應用。

.NET MAUI單項目

.NET MAUI應用通常由一個可以針對安卓、iOS、macOS和Windows的項目組成。這提供了以下好處:

  • 一個針對多個平台和設備的項目。
  • 一個位置來管理資源,如字體和圖像。
  • 多目標組織平台特定代碼。

image

image

支持平台

.NET多平台應用UI(MAUI)應用可用於以下平台編寫:

  • Android 5.0 (API 21) or higher.
  • iOS 10 or higher.
  • macOS 11 (Big Sur) or higher.
  • Windows desktop and the Universal Windows Platform (UWP), using Windows UI Library (WinUI) 3.

適用於安卓、iOS和視窗的NET MAUI應用程序可在視覺工作室內置。但是,使用最新版本的Xcode和Apple所需的macOS的最小版本進行iOS開發需要聯網Mac。

適用於安卓、iOS和macOS的NET茂宜島應用程序可在Mac視覺工作室內置。

前置條件

創建MAUI的前置條件是.Net的版本必須大於等於.NET 6 Preview 5

而且Visual Studio版本必須大於等於Visual Studio 16.11 Preview 2

image

如果要創建MAUI For Windows App,還需要額外安裝兩個擴展:

安裝.NET 6 Preview

我們可以使用官方提供的一個工具來檢查我們需要運行MAUI的環境是否齊全,還是很貼心哈。

https://github.com/redth/dotnet-maui-check

安裝MAUI環境檢查工具(Maui.Check)

dotnet tool install -g Redth.Net.Maui.Check

image

運行MAUI環境檢查工具(Maui.Check)

maui-check

image

然后會彈出這個檢查工具的命令行界面,根據提示,一步步把缺失的進行安裝和補充即可。

如果發現打開maui-check工具后,立即閃退,據說改用管理員權限重新打開終端再執行能解決這個問題。

image

如果接下來,你繼續遇到了raw.githubusercontent.com:443的報錯,要么全局代理,要么手動下載並且指定文件maui.manifest.json了。

maui-check -m maui.manifest.json

image

要命的是,如果你還是繼續遇到raw.githubusercontent.com:443的報錯,估計是在maui.manifest.json有個文件你過不去,最好也是手動下載並且寫死本地路徑最好。文件是:Versions.props

image

恭喜,這次算是過去了。

image

這里默認推薦你安裝OpenJDK 11了,而且還是給你下載由微軟構建的Microsoft OpenJDK 11

image

發現少了安卓SDK,那就Fix吧!

image

發現少了.net 6全新的工作負載.NET SDK - Workloads,那就Fix吧!

image

發現少了MAUI SDK全家桶,各個平台的都要,那就Fix吧!

image

終於,可以愉快的結束了,謝謝你哦!

image

再執行一次,看看全貌!確認下勝利的果實。

image

體驗第一個MAUI項目

https://github.com/TaylorShi/HelloMaui

創建第一個MAUI項目“HelloMaui”

創建MAUI項目的方式有兩種,一種是基於Visual Studio 2019創建,一種是基於DotNet Core CLI創建。

1. 基於Visual Studio 2019創建。

image

image

image

image

image

image

2. 基於DotNet Core CLI創建。

dotnet new maui -n HelloMaui

基於DotNet-Cli構建工具new關鍵詞新建maui項目模板的項目,項目名稱為HelloMaui

image

然后切換到項目目錄

cd HelloMaui

image

用Visual Studio Code打開它。

code .

image

還原初創建項目

dotnet restore

image

構建初創項目

dotnet build -t:Run -f net6.0-android
dotnet build -t:Run -f net6.0-ios
dotnet build -t:Run -f net6.0-maccatalyst

如果是IOS項目,還可以選擇模擬器:

dotnet build -t:Run -f net6.0-ios -p:_DeviceName=:v2:udid=<UDID>

運行第一個MAUI項目“HelloMaui”

折騰半天,發現用Visual Studio Code還是有點玩不裝MAUI,還是轉站Visual Studio 2019 Preview吧!

先用Visual Studio 2019 Preview打開Hello MaUI項目。

image

從結構上看,貌似是拆分成了WINUI項目和非WINUI項目,非WINUI項目主要是放Android、Linux、IOS平台的。

image

切換到Hello MAUI這個默認的選項后,運行,上來就是熟悉的,讓你創建安卓模擬器配置了。

image

根據提示,乖乖就范吧,不然怕出什么幺蛾子。

image

等它下載完畢后,我們啟動它,一般調試安卓都是要先開一個模擬器或者真機,這個流程我們還是知道的。

image

我的硬件還算爭氣,沒出幺蛾子,直接就運行成功模擬器了。

image

接下來,根據實操經驗,建議重啟一次Visual Studio,這樣它比較更好的識別我們的模擬器。

切換到Android Emulator模式,跑起來吧。

image

哈哈,不錯,算是開始順利,畢竟是成功部署進去了。

image

打量第一個MAUI項目“HelloMaui”

我們先順着官方指引,看看MAUI對包的依賴。

image

<ItemGroup>
    <PackageReference Include="Microsoft.Maui" Version="6.0.100-preview.5.794" />
</ItemGroup>

image

<ItemGroup>
    <PackageReference Include="Microsoft.ProjectReunion" Version="0.8.0-preview" />
    <PackageReference Include="Microsoft.ProjectReunion.Foundation" Version="0.8.0-preview" />
    <PackageReference Include="Microsoft.ProjectReunion.WinUI" Version="0.8.0-preview" />
    <FrameworkReference Update="Microsoft.Windows.SDK.NET.Ref" RuntimeFrameworkVersion="10.0.19041.16" />
    <FrameworkReference Update="Microsoft.Windows.SDK.NET.Ref" TargetingPackVersion="10.0.19041.16" />
</ItemGroup>

安卓模式下,能看到你的模擬器實例,到時候調試也是可以選擇的。

image

關於應用的資源設置,我們也可以找到線索,可以看到貌似為了保持多個平台一致性,官方的案例里面就直接用SVG這種矢量格式了,其實這還是蠻值得推薦的,就是設計師要注意輸出這種格式給開發了。

image

<ItemGroup>
    <!-- App Icon -->
    <MauiImage
        Include="Resources\appicon.svg"
        ForegroundFile="Resources\appiconfg.svg"
        IsAppIcon="true"
        Color="#512BD4" />

    <!-- Splash Screen -->
    <MauiSplashScreen Include="Resources\appiconfg.svg" Color="#512BD4" />

    <!-- Images -->
    <MauiImage Include="Resources\Images\*" />

    <!-- Custom Fonts -->
    <MauiFont Include="Resources\Fonts\*" />
</ItemGroup>

針對不同平台的代碼是分了文件夾放置的,這點和React Native還是挺類似的。

image

啟動程序代碼還是比較按最新的.Net Core的規范來,這里有個UseMauiApp,應該是指定應用的根視圖的,加載字體的話,可以用ConfigureFonts來定制。

public class Startup : IStartup
{
    public void Configure(IAppHostBuilder appBuilder)
    {
        appBuilder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            });
    }
}

既然啟動的時候,主動找了App,那么我們就看下App內部做了什么,這里設置了下資源文件目錄,然后啟動了一個Microsoft.Maui.Controls.Window類型的窗體。

image

public partial class App : Application
{
    public App()
    {
        InitializeComponent();
    }

    protected override IWindow CreateWindow(IActivationState activationState)
    {
        this.On<Microsoft.Maui.Controls.PlatformConfiguration.Windows>()
            .SetImageDirectory("Assets");

        return new Microsoft.Maui.Controls.Window(new MainPage());
    }
}

前往MainPage,我們一看究竟,哇,這不就是對Windows開發童鞋最熟悉的Xaml,是不是很爽!這里只是控件全部基於Microsoft.Maui.Controls重新來了一套嘛,其實很熟悉了。

image

image

using System;
using Microsoft.Maui.Controls;

namespace HelloMaui
{
	public partial class MainPage : ContentPage
	{
		public MainPage()
		{
			InitializeComponent();
		}

		int count = 0;
		private void OnCounterClicked(object sender, EventArgs e)
		{
			count++;
			CounterLabel.Text = $"Current count: {count}";
		}
	}
}

我們再看看WINUI項目的內容,發現其實是個空殼子,所有的實現都不在這里,這里就是原來UWP那套應用配置項而已。

image

學習MAUI項目的基本玩法

1. 學習MAUI的啟動入口規范。

首先.NET Multi-platform App UI (MAUI)它是遵循了.NET 通用主機(.NET Generic Host)的啟動規范的,這個啟動入口的設計,就在主項目的Startup.cs文件中。

在這個Startup.cs中必須存在一個對IStartup接口方法的實現,並且IAppHostBuilder必須至少添加一個應用的實現才可以跑起來,這里舉例就是appBuilder.UseMauiApp<App>()這個。

using Microsoft.Maui;
using Microsoft.Maui.Hosting;

public class Startup : IStartup
{
    public void Configure(IAppHostBuilder appBuilder)
    {
        appBuilder.UseMauiApp<App>();
    }
}

然后在App中,又必須存在一個對Application方法的繼承實現,並且重寫CreateWindow方法,而且至少實現一個Window的創建返回。

using Microsoft.Maui;
using Microsoft.Maui.Controls;

public partial class App : Application
{
    protected override IWindow CreateWindow(IActivationState activationState)
    {
        return new Window(new MainPage());
    }
}

這里舉例的MainPage窗體,是一個繼承自ContentPage的實現者。

public partial class MainPage : ContentPage
{
    public MainPage()
    {
        InitializeComponent();
    }
}

2. 在MAUI中玩轉自定義字體

如果需要注冊新的字體加到應用中,可以在Startup.csConfigure方法中,通過ConfigureFonts來添加。

using Microsoft.Maui;
using Microsoft.Maui.Hosting;

public class Startup : IStartup
{
    public void Configure(IAppHostBuilder appBuilder)
    {
        appBuilder
            .UseMauiApp<App>()
            .ConfigureFonts(fonts =>
            {
                fonts.AddFont("Lobster-Regular.ttf", "Lobster");
            });
    }
}

IFontCollection對象的AddFont方法中,第一個參數是字體的全稱路徑,第二個參數是這個字體的別名,並且所有定制字體都必須描述進項目的描述文件(.csproj)中,這里可以直接用目錄 + *來包括整個目錄的定制字體。

<ItemGroup>
   <MauiFont Include="Resources\Fonts\*" />
</ItemGroup>

添加的字體,可以直接在Xaml里面,通過指定FontFamily來使用,可以使用無格式后綴的文件名全稱,也可以用字體的別名。

<!-- Use font name -->
<Label Text="Hello .NET MAUI"
       FontFamily="Lobster-Regular" />
<!-- Use font alias -->
<Label Text="Hello .NET MAUI"
       FontFamily="Lobster" />

說了這么多,還是找個自定義字體試試,這里想到了自定義圖標字體,從https://icofont.com/icons 找了一組品牌的圖標字體,里面有微軟的圖標,下載后把ttf文件重命名下為Brand-IconFont.ttf,然后拖到項目的\Resources\Fonts文件夾下即可。

image

Startup.cs中,添加對這個字體的注冊,別名取為BrandIcoFont吧。

public void Configure(IAppHostBuilder appBuilder)
{
    appBuilder
        .UseMauiApp<App>()
        .ConfigureFonts(fonts =>
        {
            fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
            fonts.AddFont("Brand-IconFont.ttf", "BrandIcoFont");
        });
}

切換到構建界面的MainPage.xaml,找個位置把使用字體的文本控件插進去。

<Label
    Grid.Row="2"
    FontFamily="BrandIcoFont"
    FontSize="32"
    HorizontalOptions="CenterAndExpand"
    SemanticProperties.HeadingLevel="Level1"
    Text="&#xe90b;" />

這里我們用別名的方式指定FontFamily為BrandIcoFont,至於Text的值,我們需要從字體網站獲取下,一般復制HTML Entity的值就好了。

image

好了,跑起來,看看效果吧,還行,基本達到預期,看到微軟Logo了。

image

3. 在MAUI中玩轉自定義事件。

如果需要將控件的自定義事件加到應用中,可以在Startup.csConfigure方法中,通過ConfigureMauiHandlers來添加。

using Microsoft.Maui;
using Microsoft.Maui.Hosting;

public class Startup : IStartup
{
    public void Configure(IAppHostBuilder appBuilder)
    {
        appBuilder
            .UseMauiApp<App>()
            .ConfigureMauiHandlers(handlers =>
            {
                handlers.AddHandler(typeof(MyEntry), typeof(MyEntryHandler));
            });
    }
}

IMauiHandlersCollection對象的AddHandler方法中,第一個參數是控件的實體名稱,第二個參數是需要綁定的自定義事件。

這樣注冊之后,所有MyEntry控件就都會綁定MyEntryHandler這個事件了。

4. 在MAUI中玩轉自定義渲染器。

如果需要將控件的自定義渲染器加到應用中,可以在Startup.csConfigure方法中,通過ConfigureMauiHandlers來添加。

using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Controls.Compatibility;

public class Startup : IStartup
{
    public void Configure(IAppHostBuilder appBuilder)
    {
        appBuilder
            .UseMauiApp<App>()
            #if __ANDROID__
            .ConfigureMauiHandlers(handlers =>
            {
                handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
                    typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.BoxRenderer));
                handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
                    typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers.FrameRenderer));
            });
            #elif __IOS__
            .ConfigureMauiHandlers(handlers =>
            {
                handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
                    typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.BoxRenderer));
                handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
                    typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.FrameRenderer));
            });
            #endif
    }
}

這里可以通過跨平台設備的if條件,來編寫,通過IMauiHandlersCollection對象的AddCompatibilityRenderer方法來添加指定的控件走什么渲染器。

注意要添加using Microsoft.Maui.Controls.Compatibility;

using Microsoft.Maui;
using Microsoft.Maui.Hosting;
using Microsoft.Maui.Controls.Hosting;
using Microsoft.Maui.Controls.Compatibility;

namespace HelloMaui
{
    public class Startup : IStartup
    {
        public void Configure(IAppHostBuilder appBuilder)
        {
            appBuilder
                .UseMauiApp<App>()
                .ConfigureFonts(fonts =>
                {
                    fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                    fonts.AddFont("Brand-IconFont.ttf", "BrandIcoFont");
                })
                #if __ANDROID__
                .ConfigureMauiHandlers(handlers =>
                {
                    handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
                        typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.BoxRenderer));
                    handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
                        typeof(Microsoft.Maui.Controls.Compatibility.Platform.Android.FastRenderers.FrameRenderer));
                });
                #elif __IOS__
                .ConfigureMauiHandlers(handlers =>
                {
                    handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.BoxView),
                        typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.BoxRenderer));
                    handlers.AddCompatibilityRenderer(typeof(Microsoft.Maui.Controls.Frame),
                        typeof(Microsoft.Maui.Controls.Compatibility.Platform.iOS.FrameRenderer));
                });
                #endif
        }
    }
}

5. 在MAUI中玩轉自定義控件。

.NET多平台應用UI(MAUI)提供可用於顯示數據、啟動操作、指示活動、顯示集合、拾取數據等的控件集合。默認情況下,處理程序將這些跨平台控件映射到每個平台上的原生控件。例如,在iOS上,Button控件將會映射成UIButton。在安卓系統上,Button控件將會映射成AppCompatButton

image

比如我們要自定義安卓平台,所有的界面背景顏色。

using Microsoft.Maui;
using Microsoft.Maui.Controls;

public partial class App : Application
{
    public App()
    {
        InitializeComponent();

#if __ANDROID__
        Microsoft.Maui.Handlers.ViewHandler.ViewMapper[nameof(IView.BackgroundColor)] = (h, v) =>
        {
            (h.NativeView as Android.Views.View).SetBackgroundColor(Microsoft.Maui.Graphics.Colors.Cyan.ToNative());
        };
#endif
    }
}

如果要移除安卓組件的下划線

using Microsoft.Maui;
using Microsoft.Maui.Controls;

public partial class MainPage : ContentPage, IPage
{
    public MainPage()
    {
        InitializeComponent();
#if __ANDROID__
        Handlers.EntryHandler.EntryMapper[nameof(IEntry.BackgroundColor)] = (h, v) =>
        {
            (h.NativeView as global::Android.Views.Entry).UnderlineVisible = false;
        };
#endif
    }
}

如果想自定義控件,可以繼承自Entry來做。

using Microsoft.Maui.Controls;

namespace HelloMaui
{
    public class MyEntry : Entry
    {

    }
}

還可以自定義一個處理給到自定義控件。

using Microsoft.Maui;
using Microsoft.Maui.Controls;
using Microsoft.Maui.Graphics;

namespace MauiApp1
{
  	public partial class App : Application
  	{
  		public App()
  		{
          InitializeComponent();

          Microsoft.Maui.Handlers.EntryHandler.EntryMapper[nameof(IView.BackgroundColor)] = (handler, view) =>
          {
              if (view is MyEntry)
              {
#if __ANDROID__
                  handler.NativeView.SetBackgroundColor(Colors.Red.ToNative());
#elif __IOS__
                  handler.NativeView.BackgroundColor = Colors.Red.ToNative();
                  handler.NativeView.BorderStyle = UIKit.UITextBorderStyle.Line;
#elif WINDOWS
                  handler.NativeView.Background = Colors.Red.ToNative();
#endif
              }
          };
  		}
  	}
}

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM