Xamarin.Forms社内勉強会

# これは何？
弊社で行った勉強会の資料を見える形にして公開したものです。

---
# 勉強会
Xamarin.Formsの勉強会です。

対象は以下のような人です。

- プログラミングをしている
- C#を触ったことがない
- Xamarinが気になる
- クロスプラットフォームアプリ開発が気になる

---
# 事前準備
- Visual Studioのインストール
- XCodeのインストール(Macの方)

もちろんしてあるよね？


してないならすぐにしよう！
インストールには結構時間かかるからね！

---
# 今日の予定

13:00 ～ 14:00

- C#とは
    - Linq
    - Xamarinとは
        - Xamarinネイティブ
        - Xamarin.Forms

14:15 ～ 15:15

- Visual Studioとは
    - プロジェクトの作り方
    - デバッグの仕方
    - nuget

---
15:30 ～ 19:00

- Xamarin.Formsアプリの構成
    - Pages
    - Layout
    - Control
    - よく使うライブラリ
- Xamarin.Formsでアプリを作ろう

---
# C#とは

---
## C#概要
- Microsoftが開発したプログラミング言語
- 文法はC言語スタイルのよくあるやつ(CとかC++とかJavaとか)
- **Linq**
- **Visual Studio**

```C#
public string GetMyBirthday()
{
    return "1991/10/03";
}
```

---
## Linq
- Language INtegrated Queryの略称
- Linq使わないならC#使わないほうがいい
- いろんなデータに対して統一的な手段でアクセスする仕組み
    - Linq to SQL
    - Linq to XML
    - Linq to Entities
    - Linq to Objects
    - Linq to DataSet
    - ...

---
### Linqをどう使うか

```C#
public class Employee
{
    public string Name { get; set; }
    public int Age { get; set; }
    public int CompanyID { get; set; }
}

private int MY_COMPANY_ID = 1;

public void Main()
{
    // すべての社員を取得
    List<Employee> AllEmployeeList = GetAllEmployee();

    // 弊社の社員を取得
    List<Employee> MyCompanyEmployeeList =
        AllEmployeeList.Where( x => x.CompanyID == MY_COMPANY_ID );

    // 弊社の30才以下の社員の名前一覧を取得
    List<string> MYCompanyEmployeeNameList =
        AllEmployeeList
            .Where( x => x.CompanyID == MY_COMPANY_ID && x.Age <= 30 )
            .Select( x => x.Name );
}
```

---
```C#
public class SoccerPlayer : Employee
{
    public string Position { get; set; }
}

public void Main()
{
    var MyCompanyEmployeeList = 
        db.EmproyeeMaster
            .Where( x => x.CompanyID == MY_COMPANY_ID )
            .OrderBy( x => x.Age )
            .Select( x => new SoccerPlayer
            {
                Name = x.Name,
                Age = x.Age,
                CompanyID = x.CompanyID,
                FavoriteColor = "goal keeper"
            });
}
```

---
## Xamarinとは
- .NETの技術でiOS・Androidアプリ「とか」が作れるSDK
- 作れるもの
    - iOSアプリ
    - Androidアプリ
    - Windowsアプリ(UWP)
    - Macアプリ
- このいろいろ作れるものの総称としてXamarinと呼んでいる
- 作り方(共通化できる範囲で分けている)
    - Xamarinネイティブ
    - Xamarin.Forms

---
### Xamarinネイティブ
- アプリのコア部分(サーバー側)を共通化できる
- 画面側は共通化できない
- つまりAndroidとiOSのフロント技術がそのまま使える

---
### Xamarin.Forms
- アプリのコア部分(サーバー側)を共通化できる
- 画面側も共通化できる
- ひとつのコードで両対応の画面が生成できる！
- つまりXamarin.Formsを使えばiOSやAndroidの知識がなくても作れるということ？

---
- 残念ながらそんなことはない
- 出力されるものはiOSやAndroidなので、画面側で固有のバグが発生した場合に特定が難しくなる可能性
- Xamarin.FormsはiOSとAndroidの最大公約数的な作り方になっている
- 片方のみに存在するAPIが実装されていなかったりする
    - この場合書き方を分ける必要があるし、それぞれのネイティブの知識がないと難しい

---
# Visual Studioとは

---
## Visual Studio概要
- microsoftが提供する統合開発環境
- C#の開発するならとにかくこれを使う
- 環境を整えるのが(比較的)簡単(になった)
    - 今回だってVisual Studioのインストールだけで事前準備終わりだしね
- **Nuget**
- Visual Studioの話だけで勉強会できるぐらいいろんなことができる
- なのでざっくり話します

---
## Visual Studioの使い方

---
### プロジェクトの作り方
[ファイル]→[新規作成]→[プロジェクト]
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147093/69d926e8-6f10-684c-1105-d33f119124cf.png)

---
[Visual C#]→[Cross-Platform]→[Cross Platform App (Xamarin)]
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147093/d7eeb174-66a3-add5-6f37-bf745025a7da.png)

---
[Blank App]
[Xamarin.Forms]
[Portable Class Library (PCL)]
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147093/4e70f9de-4f12-bf86-8b3b-b86b02ee744b.png)

---
### デバッグの方法
- デバッグボタンを押すだけ
- Xamarin.Formsの場合、Androidのエミュレータが起動します
- その上で作成したアプリケーションが動きます

---
このボタンをまだ押してはいけない！！！！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147093/84e4464c-26d1-d55b-f281-bb617e752312.png)

---
その前に変更しましょう！！！！！
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147093/0e6e9e6d-d193-20f8-eaec-50f19b600ded.png)
右の「▼」を押すとリストが開くので変更しましょう。ARMはヤバイです。

---
#### どれぐらいARMヤバイの？
- 実行速度(起動速度)に10倍の差があると言われています
    - 実際にこういう警告文をVisual Studioさんが出します
- 体感だとそれ以上遅いような気さえする
- とにかく遅いヤバイ

---
### Nugetとは
- Visual Studioのパッケージ管理ツール
- [プロジェクト]→[Nugetパッケージの管理]
- いろんなパッケージを入れたり消したりできる
- ビルド時に足りないパッケージを復元してくれる
- わかりやすいので好き

---
# Xamarin.Formsでアプリを作るには

---
## Xamarin.Formsアプリの構成
作成時点では次のようになっている
- PCL
- Xamarin.Android
- Xamarin.iOS

---
- PCLがXamarin.AndroidやXamarin.iOSから参照されて画面作成等している
- 基本的に触るのはPCLのみ
- アプリが動くとApp.xaml.csがまず動く
    - どれを最初に動かそうとしているかはXamarin.AndroidのMainActivity.csを見よう

---
App.xaml.cs
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147093/f3dd87ba-a79e-6416-72f7-ea9fdc06554f.png)
1. `InitializeComponent();`
2. MainPageを描画する画面として設定

---
Main.xaml
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147093/0129e07c-afa8-be0c-239a-1ce228272528.png)
デバッグ実行したときに見えた「Welcome to Xamarin.Forms!」はここ

---
### Pages
- Pagesクラスは画面のほとんどを占有する、画面の基本的なクラス
- URLを見ながら解説します

とても参考になるページ
https://dev.classmethod.jp/smartphone/xamarin-forms-page/

---
### Layout
- Layoutクラスはページやビュー上に複数のコントロールを配置するためのコンテナ
- URLを見ながら解説します

とても参考になるページ
https://dev.classmethod.jp/smartphone/xamarin-forms-layout/

---
### Control
- ControlクラスはPages、Layoutの上に乗せていくコントロール
- Xamarin.Formsのフレームワークの中では、「Views」と言う名前でカテゴライズされている
- URLの中からいくつかピックアップしてお話します

とても参考になるページ
https://dev.classmethod.jp/smartphone/xamarin-forms-control1/
https://dev.classmethod.jp/smartphone/xamarin-forms-controll2/

---
### データバインディング
- Xamarin.Formsにはデータバインディングを行う機能がある
- 強力な機能なのでXamarin.Forms開発者は全員使うべき
- デモします

---
```C#
public class Employee : INotifyPropertyChanged
{
    private string _name;
    public string Name 
    {
        get { return _name; }
        set
        {
            _name = value;
            OnPropertyChanged("Name");
        }
    }
    
    ...

    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged == null) return;

        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
}
```
わざわざOnPropertyChangedの実装をしないといけない…
めんどくさい…

---
```C#
using PropertyChanged;

public class Employee : INotifyPropertyChanged
{
    public string Name { get; set; }
    
    ...
    
    // eventの宣言は必要
    public event PropertyChangedEventHandler PropertyChanged;
    /* いらない
    protected virtual void OnPropertyChanged(string name)
    {
        if (PropertyChanged == null) return;

        PropertyChanged(this, new PropertyChangedEventArgs(name));
    }
    */
}
```
PropertyChanged.Fodyを使って簡略化！！

---
### よく使うライブラリ
- PropertyChanged.Fody
    - プロパティが変更されたことを自動で通知してくれるライブラリ
    - これを使わないと画面にバインドするModelの中身が結構大変なことになる
    - インストールした後にFodyを最新にアップデートすること
- sqlite-net-pcl
    - Xamarin.FormsでSQLiteを使いたいときはこれ
- PCL Storage
    - Xamarinのファイル操作ライブラリ
    - sqlite-net-pclと組み合わせることでPCL側だけでDB操作できる
    - http://www.nuits.jp/entry/2016/06/27/191636

---
# Xamarin.Formsでアプリを作ろう！

---
## 作るもの
- ドラフトタイマー

---
## 仕様
- ボタンを押してタイマースタート
- 現在何パック目かの音声出力(他、適宜必要な場所で音声を入れる)
- 現在何パック目、何ピック目かを表示しておく
- ドラフトの時間は1ピック目から順に以下の通り(単位:秒)
    - { 40, 35, 30, 25, 25, 20, 20, 15, 10, 10, 5, 5, 5, 5 }
- ドラフトのパックとパックの間にはインターバルを取る
    - 1回目 60秒
    - 2回目 90秒

---
### タイマーの実装に役立つもの

---
#### デバイスのタイマー機能
```C#
Device.StartTimer(Timespan interval, Func<>());
```
```C#
Device.StartTimer(
    TimeSpan.FromSeconds(1),
    () =>
    {
        // タイマー操作;
    });
```
Func<>の部分でtrueを返すとTimerは継続して実行される。
falseが返されるとTimer終了。

---
#### デバイスの音声読み上げ機能
- Xamarin.Formsのデフォルトの機能として用意されていない
- Android、iOSにそれぞれの処理を書く必要がある

作る手順は以下。
1. PCLにinterfaceを作成する
2. Project.Androidに1.のinterfaceを継承したclassを作成する
3. Project.iOSに1.のinterfaceを継承したclassを作成する

---
```C#
public interface ITextToSpeech
{
    void Speak(string text);
}
```
PCLに`ITextToSpeech`interfaceを作成する。

---
```C#
[assembly: Dependency(typeof(TextToSpeechImplementation))]
namespace DraftTimeManager.Droid
{
    public class TextToSpeechImplementation : Java.Lang.Object, ITextToSpeech, TextToSpeech.IOnInitListener
    {
        private TextToSpeech speaker;
        private string toSpeak;

        public void Speak(string text)
        {
            toSpeak = text;
            if (speaker == null)
            {
                speaker = new TextToSpeech(Forms.Context, this);
            }
            else
            {
                speaker.Speak(toSpeak, QueueMode.Flush, null, null);
            }
        }

        public void OnInit(OperationResult status)
        {
            if (status.Equals(OperationResult.Success))
            {
                speaker.Speak(toSpeak, QueueMode.Flush, null, null);
            }
        }
    }
}
```
Project.Androidに`ITextToSpeech`を継承させた`TextToSpeechImplementation`を作成する。

---
```C#
[assembly: Dependency(typeof(TextToSpeechImplementation))]
namespace DraftTimeManager.iOS
{
    public class TextToSpeechImplementation : ITextToSpeech
    {
        public TextToSpeechImplementation() { }

        public void Speak(string text)
        {
            var speechSynthesizer = new AVSpeechSynthesizer();
            var speechUtterance = new AVSpeechUtterance(text)
            {
                Rate = AVSpeechUtterance.MaximumSpeechRate / 2,
                Voice = AVSpeechSynthesisVoice.FromLanguage("en-US"),
                Volume = 0.5f,
                PitchMultiplier = 1.0f
            };

            speechSynthesizer.SpeakUtterance(speechUtterance);
        }
    }
}
```
Project.iOSに`ITextToSpeech`を継承させた`TextToSpeechImplementation`を作成する。

---
# 参考資料

https://qiita.com/amay077/items/38ee79b3e3e88cf751b9
http://furuya02.hatenablog.com/entry/20140523/1400966058
https://developer.xamarin.com/guides/xamarin-forms/getting-started/