【IoT】ESP8266で温湿度取得してスマホでモニタリング 第2回

#はじめに
[【IoT】ESP8266で温湿度取得してスマホでモニタリング 第1回](http://qiita.com/pocota5260/items/c363d32ff0f781d2ec38)に引き続き、第2回はAndroidアプリ側を制作したので投稿します。
グラフはネットで調べたところ、MPAndroidChartが広く使われている(？)ようだったので使ってみました。

#Xamarin.Android
開発環境は、前回に引き続きVisual Studio Community 2017を使っています。
[Xamarin.Androidインストール手順](http://ytabuchi.hatenablog.com/entry/visualstudio2017)
[公式サンプルソース](https://msdn.microsoft.com/ja-jp/library/dn879698.aspx)

#MPAndroidChartライブラリ
Xamarinで使えるMPAndroidChartはこちらからソースをダウンロードできます。
[ダウンロードはここからgithub](https://github.com/capuzzi/Xamarin-Forms-MP-Android-Chart-library)
ダウンロード後、ソースをビルドすると各種DLLが生成させるようです。
（私の環境では普通にビルド通りましたけどどうなんでしょうか）
今回は、\packages\MPAndroidChart.3.0.0\lib\MPAndroidChart.dllを使いました。
Xamarin版のサンプルソースがJava版と比較して圧倒的に少ないのが難点ですね。

#MQTTライブラリ
MQTTはC#で使えるライブラリを適当にチョイスしました。
[ダウンロードはここからgithub](https://github.com/er230059/MQTT-Client/tree/master/MQTT-Client)
ダウンロード後、M2Mqtt.dllを使いました。
すぐ使えるアプリケーションもセットになっているので、使い易いかと思います。

#作製したアプリの各画面
##メイン画面
![Screenshot_20170914-230707_compressed.jpg](https://qiita-image-store.s3.amazonaws.com/0/189598/71226636-4e6e-a7f4-695a-514329615075.jpeg) 

##設定画面
![Screenshot_20170914-230630_compressed.jpg](https://qiita-image-store.s3.amazonaws.com/0/189598/856b646a-be33-b526-03a9-832ebc78c652.jpeg) ![Screenshot_20170914-230641_compressed.jpg](https://qiita-image-store.s3.amazonaws.com/0/189598/08802c11-b71b-4944-149b-1546768707d2.jpeg) ![Screenshot_20170914-230656_compressed.jpg](https://qiita-image-store.s3.amazonaws.com/0/189598/e193761e-ed7d-1412-f45d-f3d00e54f9f9.jpeg)



#開発で行き詰った事
##NumberPickerはマイナスが表示できない問題
温度・湿度のスケールを設定するダイアログで、当初はNumberPickerで作っていたましたが、いざ作ってみるとマイナスが表示されないことに気づきました。仕方ないので、普通にListViewで選択する方向で作ってみましたが、なんかダサいですね。
後々調べてみると、NumberPickerをカスタムして使う投稿もあったみたいですね。
[NumberPickerをカスタムして使う](http://qiita.com/ksbababa/items/07ab469c28545becd949)

##ポップアップをクローズしたときに親画面に値を返せない問題
AlertDialogクラスにはクローズ時のイベントOnDismissが用意されていますが、さすがに親に値を返すイベントまではないようです。
仕方ないので、クローズしたときに呼び出されるコールバック関数を用意しました。

```ListDialogFragment.cs
    public class DialogEventArgs : EventArgs
    {
        public string ReturnValue { get; set; }
    }

    /// <summary>
    /// リストダイアログ表示クラス
    /// </summary>
    public class ListDialogFragment : DialogFragment
    {
        public event EventHandler<DialogEventArgs> DialogClosed;
        public delegate void DialogEventHandloer(object sender, DialogEventArgs e);

        //処理内容省略...

        /// <summary>
        /// ダイアログクローズ時のイベント
        /// </summary>
        /// <param name="dialog"></param>
        public override void OnDismiss(IDialogInterface dialog)
        {
            base.OnDismiss(dialog);

            // 呼び出した親に値を返す
            if (DialogClosed != null)
            {
                DialogClosed(this, new DialogEventArgs { ReturnValue = _selectedValue });
            }
        }
    }
```

ダイアログを呼び出す親画面でDialogClosedにコールバック関数を設定してあげると、ダイアログをクローズしたときにコールバック関数が呼び出されます。尚、値はDialogEventArgs eのReturnValueプロパティにセットされています。

```SettingActivity.cs
        protected override void OnCreate(Bundle savedInstanceState)
        {
            //処理内容省略...

            // 室温最小値ボタンのクリックイベント
            btnTempMin.Click += (_, __) =>
            {
                dialogKey = TempMinKey;
                // ダイアログ表示
                ListDialogFragment dlg = new ListDialogFragment(
                                                this,
                                                "室温(最小)を選択してください",
                                                Resource.Array.TempAxisItems,
                                                Resource.Array.TempAxisValues);
                dlg.DialogClosed += OnDialogClosed;     // 選択時のコールバック関数
                dlg.Show(this.FragmentManager, typeof(ListDialogFragment).Name);
            };
            //処理内容省略...
        }

        /// <summary>
        /// ダイヤログ選択時のコールバック関数
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void OnDialogClosed(object sender, DialogEventArgs e)
        {
            //処理内容省略...
        }
```

##MPAndroidChartのX軸のラベルをカスタムする方法
MPAndroidChartのX軸のラベルはデフォルトでは、0,1,2…のようにインデックスで表示されます。
カスタムラベルする方法は、Javaでは結構サンプルソースがあるのですが、Xamarin.Androidでは中々見つからないので苦労しました。
結局のところ、IAxisValueFormatterを継承したクラスを作らないといけないようです。
以下のソースでは、IAxisValueFormatterを継承したXLabelFormatterクラスを用意し、GetFormattedValueでラベルを作成します。
”if (int.TryParse(value.ToString("0.#"), out val) == true)”をしているのは、整数チェックをしています。というのは、引数のfloat valueには少数で値が入ってくることがあるようです。(なぜかは不明)
また、”if (labelList.Count > val && val >= 0)”は、グラフに表示するデータ件数が0件でもグラフの枠だけ表示し、ラベルは何も表示したくないので、このような処理を入れています。
XLabelFormatterでは、addLabelでX軸に表示したいラベルをlabelListにaddし、GetFormattedValueでX軸のインデックスに対応したラベルをlabelListから取得しています。

```MainActivity.cs
    public class MainActivity : Activity
    {
        //処理内容省略...
        XLabelFormatter xformatter = new XLabelFormatter();
        private void setData(string xlabel, string temp, string humi)
        {
            // X軸の設定
            XAxis xAxis = mChart.XAxis;
            xAxis.SetLabelCount(tempDataset.EntryCount, true);
            xformatter.addLabel(xlabel);
            xAxis.ValueFormatter = xformatter;
            //処理内容省略...
        }

        //処理内容省略...
    }

    public class XLabelFormatter : Java.Lang.Object, IAxisValueFormatter
    {
        private static List<string> labelList = new List<string>();

        public int DecimalDigits => 0;

        public void addLabel(string item)
        {
            labelList.Add(item);
        }

        public string GetFormattedValue(float value, AxisBase axis)
        {
            int val = 0;
            string ret = "";
            if (int.TryParse(value.ToString("0.#"), out val) == true)
                if (labelList.Count > val && val >= 0)
                    ret = labelList[val];
            return ret;
        }
    }
```

#おわりに
今回はArduinoもXamarinもAndroidも初めてづくしで調べなたら開発したので、かなり時間かかってしまった印象です。
Arduinoではセンサー系を試してみたので、次はモータ系を試してみたいですね。
構想としては、Arduino＋ROS＋ドローン＋PS4コントローラとかやってみたいなぁ・・・
と考えているので、進捗が出ましたらまた投稿したいと思います。

#関連記事
[【IoT】ESP8266で温湿度取得してスマホでモニタリング 第1回](http://qiita.com/pocota5260/items/c363d32ff0f781d2ec38)

#ソース
準備中...少々お待ちください。