.NET MAUIによるクロスプラットフォーム2D描画(その2)

過去の記事の段階では色々と未実装機能が多かった .NET MAUI。2022/5/23についにリリースされましたので続きとして試してみました。※.NET MAUI を試すには Visual Studio 2022 v17.3 Preview 1.1 以降が必要です。

プロジェクト作成

インストール方法などはいくつも参考になる記事がでていましたので割愛します。

プロジェクトテンプレートが行方不明になる問題などもありましたが、ついに公式から正しい回避方法がでてました。global.json で sdk のバージョンを明示的に指定してからコマンドラインで作成しろとのことです。

global.jsonを以下の内容でプロジェクトを作成するフォルダに配置します。

{
  "sdk": {
    "version": "6.0.300",
    "rollForward": "latestMinor"
  }
}

コマンドラインで以下のコマンドを実行します。

dotnet new maui -n <プロジェクト名>

「テンプレート “.NET MAUI アプリ” が正常に作成されました。」と表示されれば成功です。Visual Studio 2022 v17.3 Preview 1.1以降で <プロジェクト名>.sln を開きます。

ついにエラーが無い状態でプロジェクトを開けました。

色々試してみる

クロスプラットフォームの動作確認

まずはそのままWindowsでデバッグ実行します。

Android (Emulator)でも試してみます。

共通のコードで動いています。素晴らしい。iPhone での動作確認は……まあいいでしょう。

.NET MAUI Shell アプリ

.NET MAUI Shell アプリという概念があるそうです。こちらによれば「.NET マルチプラットフォーム アプリ UI (.NET MAUI) シェルは、ほとんどのアプリで必要とされる基本的な機能を提供することで、アプリ開発の複雑さを軽減します。」ということだそうです。TabBar, Tab, FlyoutItem, Shell.SearchHandler などを使うとよく見るアプリのUIが簡単に作れるとのこと。これは便利そう。

まずはTabで囲って元々あったShellContentをコピペして貼り付けます。

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="MauiApp1.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MauiApp1"
    Shell.FlyoutBehavior="Disabled">
    <Tab>
        <ShellContent
            Title="Home"
            ContentTemplate="{DataTemplate local:MainPage}"
            Route="MainPage" />
        <ShellContent
            Title="Home"
            ContentTemplate="{DataTemplate local:MainPage}"
            Route="MainPage" />
    </Tab>
</Shell>

タブが増えてます。カウントアップされていく [Click me] の数値がタブごとに独立しており、各ページがそれぞれ別のインスタンスになっていることが確認できます。

TabBarを試してみます。わかりにくいのでHome1, Home2と表示名を変えました。

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="MauiApp1.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MauiApp1"
    Shell.FlyoutBehavior="Disabled">
    <TabBar>
        <ShellContent
            Title="Home1"
            ContentTemplate="{DataTemplate local:MainPage}"
            Route="MainPage" />
        <ShellContent
            Title="Home2"
            ContentTemplate="{DataTemplate local:MainPage}"
            Route="MainPage" />
    </TabBar>
</Shell>

下部にタブができてます。Iconを指定するとアイコンまで表示されるようです。

※公式には「1つの TabBarオブジェクト内に複数の Tabオブジェクトがある場合、Tabオブジェクトは下部のタブとしてレンダリングされます。」とありましたが、コード例のようにTabBar直下にShellContentを並べないと下部のタブとしてレンダリングされませんでした。

Windowsで実行した場合はこんな感じ。

クロスプラットフォーム2D描画

恐らくあまり需要はありませんが、当社は地図を扱っているので描画系は試したくなります。今回はGraphicsViewを使った描画を試します。.NET MAUI では、Microsoft.Maui.Graphicsというクロスプラットフォームグラフィックスキャンバスが提供されています。

PaintPageという名称で .NET MAUI ContentPage (XAML) を追加し、AppShell.xaml で追加したページが表示されるように設定します。

<?xml version="1.0" encoding="UTF-8" ?>
<Shell
    x:Class="MauiApp1.AppShell"
    xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:local="clr-namespace:MauiApp1"
    Shell.FlyoutBehavior="Disabled">
    <TabBar>
        <ShellContent
            Title="Home"
            ContentTemplate="{DataTemplate local:MainPage}" />
        <ShellContent
            Title="Paint"
            ContentTemplate="{DataTemplate local:PaintPage}" />
    </TabBar>
</Shell>

2D描画にはGraphicsViewを使用します。GraphicsViewは描画されるコンテンツとしてIDrawableの実装クラスを指定する必要があります。今回は黄色の矩形を描画する実装をしてみました。色の指定はICanvas.FillColorなどのICanvasのプロパティで指定する仕様で、FillRectanglePenBrushを指定できないのが少し気持ち悪いです。

public class GraphicsDrawable : IDrawable
{
    public void Draw(ICanvas canvas, RectF dirtyRect)
    {
        canvas.FillColor = Colors.Yellow;
        canvas.FillRectangle(100, 100, 100, 100);
    }
}

次に PaintPage.xaml で描画用のリソースとして先程作成したGraphicsDrawableを定義し、GraphicsViewのDrawableに指定します。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiApp1"
             x:Class="MauiApp1.PaintPage"
             Title="PaintPage">
    <ContentPage.Resources>
        <local:GraphicsDrawable x:Key="drawable" />
    </ContentPage.Resources>
    <StackLayout>
        <GraphicsView Drawable="{StaticResource drawable}"/>
    </StackLayout>
</ContentPage>

おおお。穴の開いたポリゴンも試してみます。FillPathWindingMode.EvenOddを指定するのがポイントです。これを指定しないとPath中の被った領域が全て塗りつぶされてしまいます。

public class GraphicsDrawable : IDrawable
{
    public void Draw(ICanvas canvas, RectF dirtyRect)
    {
        var path = new PathF();
        path.AppendRectangle(new RectF(100, 100, 150, 150));
        path.AppendRectangle(new RectF(150, 150, 50, 50));
        canvas.FillColor = Colors.Green;
        canvas.FillPath(path, WindingMode.EvenOdd);
    }
}

期待通り穴あきポリゴンを描画できました。Windowsでも確認します。

しっかり描画されていますね。ちなみに Windows ではGraphicsViewHeightRequestを定義してあげないと描画されませんでした。上画像の完全な xaml は以下になります。

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:MauiApp1"
             x:Class="MauiApp1.PaintPage"
             Title="PaintPage">
    <ContentPage.Resources>
        <local:GraphicsDrawable x:Key="drawable" />
    </ContentPage.Resources>
    <StackLayout>
        <GraphicsView Drawable="{StaticResource drawable}"
            HeightRequest="300"
            WidthRequest="400"/>
    </StackLayout>
</ContentPage>

念のためってことで実機として Google Pixel 6 Pro で試してみました。

しっかり動いています。ダークモードにも勝手に対応しています。

まとめ

まだ少し触った程度ではありますが、.NET MAUI は期待以上です。特に.NET MAUI Shell アプリが素晴らしい。よくあるUIで構成されたクロスプラットフォームアプリを作成する工数が大幅に短縮されそうです。2D描画については少し手間ですが簡単なものなら描画できそうです。SkiaSharpの方が良さげな気がしているので、今後はそちらも検証してみたいと思います。

コメントを残す

メールアドレスが公開されることはありません。

CAPTCHA