Visual Studio for Mac での Xamarin.Forms (iOS/ Android アプリ開発) 入門 02 : 新規作成したプロジェクトの構成を見てみる ＆ 簡単な画面更新してみる

今回は、どんなファイルが生えたのか目を通すのと、少し UI をいじってみるのをやりましょう。

前回の記事『[Visual Studio for Mac での Xamarin (iOS/ Android) 入門 01 : ハローワールド (新規作成、エミュレータ作成、アプリ実行)](https://qiita.com/chomado/items/6fb4c92ec5844e63abe6)』

# この記事の目標

* 自動生成されたプロジェクトの中を見て、どんなファイルがあるか確認する
* 重要なファイルについてはコードに目を通す。 
 * `App.xaml`：Xamarin.Forms の最初のエントリーポイント。ここで MainPage.xaml が呼ばれている
 * `MainPage.xaml`：アプリの最初に出て来る画面の UI が記述されている 
 * `MainActivity.cs`：Android アプリ起動時に内部的に最初に呼ばれる人
 * `AppDelegate.cs`：iOS アプリのライフサイクルを管理している人
 * `AndroidManifest.xml`：Android アプリ設定ファイル
 * `Info.plist`：iOS アプリ設定ファイル
* アプリの UI を定義している XAML ファイルを編集し、iOS/ Android アプリの画面を編集する



# Reference

* [公式] [Visual Studio for Mac](https://visualstudio.microsoft.com/vs/mac/?WT.mc_id=docs-qiita-machiy)
* [公式] [Xamarin.Forms クイックスタートの詳細 (macOS)](https://docs.microsoft.com/ja-jp/xamarin/get-started/quickstarts/deepdive?pivots=macos&WT.mc_id=docs-qiita-machiy)
* [公式] [Xamarin ドキュメントトップページ](https://docs.microsoft.com/ja-jp/xamarin/?WT.mc_id=docs-qiita-machiy)
* [公式] [Xamarin.Forms の GitHub レポジトリ](https://github.com/xamarin/Xamarin.Forms)
* [公式] 2020/04/08 時点での最新の VS for Mac のリリースノート (英語)：[What’s New for Xamarin Developers in Visual Studio 2019 for Mac version 8.5](https://devblogs.microsoft.com/xamarin/mac-version-8-5-vs19/?WT.mc_id=docs-qiita-machiy)
* [公式] [XAML Hot Reload for Xamarin.Forms (Xamarin.Forms の XAML ホットリロード)](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/hot-reload?WT.mc_id=docs-qiita-machiy)
* この記事をもとにしたセッションのスライド：『[15 分間で入門！ macOS で iOS/Android アプリを作る！ [Xamarin.Forms/ Visual Studio for Mac 入門]](https://www.slideshare.net/chomado/20204xamarin-mac)』

# プロジェクトの構成

VS for Mac の左のソリューションバーを見てみると、
この、新規作成したプロジェクト (ソリューション) の中には 
3 つのプロジェクトが含まれていることが分かります

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/c6dfeb7d-9ff8-db85-d48b-bc941f894a05.png)

＃|プロジェクト名|説明
---|---|---
1|TestApp|共通コード。全コードの 9 割くらいがここに入る。画面 UI 記述などもここ
2|TestApp.Android|Android 版の固有のコードの置き場所
3|TestApp.iOS|iOS 版の固有のコード置き場

それぞれ見てみましょう

## 共通プロジェクト `TestApp`

一番上に生えた `TestApp` のツリーを開くと、このようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/45500283-4124-4035-aaf3-98a109996695.png)

**XAML** (ザムル) とは、`Extensible Application Markup Language` の略で、アプリ UI などを定義するための XML ベースのマークアップ言語のことです。

### App クラス (`App.xaml` + `App.xaml.cs`)

`App.xaml` のツリーを開くと、コードビハインドの `App.xaml.cs` が現れます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/9a272217-d711-1e12-ad34-43e1abe7e373.png)

`App.xaml` と `App.xaml.cs` 両方（ペア）で、
ひとつの `App` クラスを定義している構成になっています。

XAML で記述した GUI に対して、 C# (など) でイベント処理を記述することができますが、そのときに、その GUI 定義ファイル (.xaml) と イベント記述のファイル (.cs) を分けて書くことができます。分けて書いているだけなので、ふたつはペアとして存在しています。

ちなみに、こうやって GUI のイベント処理を別ファイルに分けて書くことを **`コードビハインド`**（code-behind）と呼びます。

この App クラスが、Xamarin.Forms のエントリーポイントとなります。

### App.xaml.cs

コードビハインドの `App.xaml.cs` を見てみましょう。

````csharp:App.xaml.cs
using System;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace TestApp
{
    public partial class App : Application
    {
        // Xamarin.Forms のエントリーポイント
        public App()
        {
            // App.xaml で定義された内容を読み込む
            InitializeComponent();
            // アプリのメインページとして、MainPage クラスのインスタンスを設定している
            MainPage = new MainPage();
        }

        protected override void OnStart()
        {
        }

        protected override void OnSleep()
        {
        }

        protected override void OnResume()
        {
        }
    }
}
````

### MainPage.xaml

この中の XAML (ザムル) ファイル `MainPage.xaml` に、アプリ起動時の画面の定義が書いてあります。

(正確に言うと、アプリのエントリーポイントは `App` クラスで、そこで起動時の画面を MainPage と指定しているから最初の画面として MainPage.xaml が呼ばれています。)



`MainPage.xaml` の中は、こうなっています。

````xml:MainPage.xaml
<?xml version="1.0" encoding="utf-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d"
             x:Class="TestApp.MainPage">
    <StackLayout>
        <!-- Place new controls here -->
        <Label
            Text="Welcome to Xamarin.Forms!"
            HorizontalOptions="Center"
            VerticalOptions="CenterAndExpand" />
    </StackLayout>
</ContentPage>
````

ContentPage の中に、
StackLayout (縦に色々詰めるレイアウト雛形) が入っていて、
その中にラベル Label が一つ、中央寄せに配置されている、という感じですね。

で、そのラベルの中に `Text="Welcome to Xamarin.Forms!"` と定義されているので、
アプリを実行したときに 「`Welcome to Xamarin.Forms!`」 と表示されたのですね。

ためしにここを「`Xamarin.Forms にようこそ`」などと変えて
もう一度実行してみると、
iOS/ Android アプリ両方が変更されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/e7e6dfd3-7b0e-255a-a6a7-8fe3f3acf41d.png)

ひとつのソースコードを変更したら、それが iOS/ Android 両方に反映されるので、
めちゃ便利ですね！
iOS 用、Android 用でコードが分かれることもなく。

今回は表示テキストを変更するだけでしたが、
当然、もっともっと複雑なことがたくさんできるわけで、
それを両方の OS 用に同時にクロスプラットフォーム開発できて便利という話になるわけです。

## Android 固有プロジェクト `TestApp.Android`

次は Android 固有プロジェクト `TestApp.Android` の中を見てみましょう。
iOS 側からは呼ばれず、Android での実行時にのみ呼ばれるメソッドなどが入っています。

`TestApp.Android` のツリーを開くと、
いくつか中身が入っているのが見えますが、
この中に、Android ネイティブ開発者には見慣れた `MainActivity` や `AndroidManifest.xml` というのが見えます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/a29dddb6-dd84-0327-b9b9-6f2065b547cd.png)

### MainActivity.cs

この MainActivity.cs を開いて、中身を見てみましょう

````csharp:MainActibity.cs
using System;

using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;

namespace TestApp.Droid
{
    [Activity(Label = "TestApp", Icon = "@mipmap/icon", Theme = "@style/MainTheme", MainLauncher = true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        // このアクティビティが立ち上がったときにまず呼ばれるメソッド
        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);

            // Xamarin 周り初期化
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());
        }
        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}
````
`OnCreate()` は、Android アプリのライフサイクルの中で、このアクティビティが立ち上がったときにまず呼ばれるメソッドです。
Android アプリ開発者なら Java や Kotlin で何度も見ているものですね。それが C# で書かれているだけです。

その OnCreate() の中で、Xamarin.Forms の初期化などが呼ばれて走っているだけとなります。


### AndroidManifest.xml

次は、設定ファイル `AndroidManifest.xml` も見てみましょう。
これも、Android アプリ開発者なら 何度も見ているファイルですね。
このマニフェストファイルにアプリの基本的な設定など（アプリアイコンどうするとか最初に呼ばれるアクティビティはどれとか）が書かれていて、これがないとアプリとして出せない重要なファイルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/b1ad9927-f193-d52a-5c61-361cd63d84c1.png)

実際は、`AndroidManifest.xml` は（拡張子の通り）XML で書かれているのですが、
VS for Mac は、それの viewer の機能があるので、上のスクショのように、いい感じに表示してくれています。（昔は XML 丸出しだったんだよ（2014 年からずっと Xamarin やってる古株なみの感想））


## iOS 固有プロジェクト `TestApp.iOS`

次は iOS 固有プロジェクト `TestApp.iOS` の中を見てみましょう。
Android からは呼ばれず iOS のみの処理が書かれています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/24609/63315969-3f28-5a8e-ef7f-dd679ce97e7c.png)

iOS アプリ開発者なら何度も見ているファイルがいくつかありますね！

### AppDelegate.cs

`AppDelegate` は、iOS アプリのライフサイクルを定義している人ですが、
ここでは「アプリが起動 (FinishedLaunching()) したら Xamarin.Forms を初期化する」という処理が呼ばれているだけとなります。

````csharp:AppDelegate.cs
using System;
using System.Collections.Generic;
using System.Linq;

using Foundation;
using UIKit;

namespace TestApp.iOS
{
    // iOS アプリのライフサイクルを管理するクラス AppDelegate
    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        // アプリが起動したときに呼ばれる
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            // Xamarin.Forms 初期化
            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }
}
````

アプリが起動したときに呼ばれる FinishedLaunching() の中で、Xamarin.Forms の初期化などが呼ばれて走っているだけとなります。

# Next Step

色々公式チュートリアルをやってみるのが良さそうですね！

* [複数ページの Xamarin.Forms アプリケーションでナビゲーションを実行する (macOS 版)](https://docs.microsoft.com/ja-jp/xamarin/get-started/quickstarts/multi-page?pivots=macos&WT.mc_id=docs-qiita-machiy)
* [ローカルの SQLite.NET データベースにデータを格納する](https://docs.microsoft.com/ja-jp/xamarin/get-started/quickstarts/database?pivots=macos&WT.mc_id=docs-qiita-machiy)


## Hot Reload もいいぞ

ちなみに、Xamarin.Forms の XAML 保存時にリアルタイムで UI 更新してくれる、Hot Reload 機能もオススメです。

→ [YouTube 動画 (ちょまどチャンネル)](https://youtu.be/di7ojNTmC_c)
→ [XAML Hot Reload for Xamarin.Forms (Xamarin.Forms の XAML ホットリロード)](https://docs.microsoft.com/en-us/xamarin/xamarin-forms/xaml/hot-reload?WT.mc_id=docs-qiita-machiy)

この 90 秒動画見ていただけたらなんとなくどんなものかつかめると思います

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/Xamarin?src=hash&amp;ref_src=twsrc%5Etfw">#Xamarin</a> .Forms の Hot Reload について<br>デモをしたのを<br>90 秒の動画にしてみました！<br><br>デバッグ実行中に<br>UI 定義ファイル XAML を編集して保存すると<br>リアルタイムに即座に反映される機能です<br><br>公式ドキュメント： <a href="https://t.co/4jj5JZLPk9">https://t.co/4jj5JZLPk9</a> <a href="https://t.co/YlwSpy8d56">pic.twitter.com/YlwSpy8d56</a></p>&mdash; ちょまど🎀ITエンジニア兼マンガ家 (@chomado) <a href="https://twitter.com/chomado/status/1254530380687802369?ref_src=twsrc%5Etfw">April 26, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>