【Xamarin.Forms】 グーグルマップと連携して検索を楽にする-完成版

こんばんは。sunn-sudoです。

前回に引き続き、ユーザインターフェイスの改良を行いました。
グーグルマップのアプリと連携して、検索を楽にする個人用Androidアプリをご紹介いたします。

前回の内容は以下の記事となりますので、ご覧ください。
https://qiita.com/sunn-sudo/items/6bef0f7169b73d4e06e2

#画面レイアウト

#メイン画面
![メイン画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951197/9c531153-9f7c-c90c-7fb9-8a1719cdab29.png)

【ボタン】
検索ワードを追加する: 検索ワード登録画面へ遷移するボタン
【リスト内】
検索ボタン: タップしたボタンのテキストをグーグルマップに連携する
削除ボタン(✖ボタン): タップしたリストを削除する

#検索ワード登録画面
![検索ワード登録画面.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/951197/3637a137-1f00-eb68-3baf-837dce064337.png)

【入力フォーム】
登録ボタン: 入力内容を検索ワードとして登録する。
※空文字の登録不可

#ソースコード

#メイン画面
```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"
             x:Class="AppGoogleMap.MainPage"
             Title="メイン画面">
    <StackLayout Margin="10">
        <Label Text="現在地周辺のスポットを検索します。" />
        <Button Text="検索ワードを追加する" Clicked="NextPage"/>
        <ListView 
        x:Name="wordList"
        HasUnevenRows="true">
            <ListView.ItemTemplate>
                <DataTemplate>
                    <ViewCell>
                        <StackLayout
                        HorizontalOptions="FillAndExpand"
                        Orientation="Horizontal">
                            <StackLayout 
                                Orientation="Vertical">
                                <Label Text="{Binding TimeStamp}" />
                                <Button Text="{Binding Word}" Clicked="OnButtonSearch" />
                            </StackLayout>
                            <Button
                            WidthRequest="40"
                            HeightRequest="40"
                            Text="✖"
                            BackgroundColor="Silver" 
                            VerticalOptions="Center"
                            HorizontalOptions="EndAndExpand"
                            CommandParameter="{Binding .}"
                            Clicked="OnButtonDelete"/>
                        </StackLayout>
                    </ViewCell>
                </DataTemplate>
            </ListView.ItemTemplate>
        </ListView>
    </StackLayout>
</ContentPage>
```

```c#:MainPage.xaml.cs
using System;
using Xamarin.Essentials;
using Xamarin.Forms;
using Plugin.Geolocator;
using Plugin.Geolocator.Abstractions;
using System.Collections.ObjectModel;

namespace AppGoogleMap
{
    public class SearchWord
    {
        [SQLite.PrimaryKey, SQLite.AutoIncrement]
        public int Id { get; set; }
        public String Word { get; set; }
        public DateTime TimeStamp { get; set; }
    }

    // リスト1件のデータを表すクラス
    public class SearchWordList
    {
        public int Id { get; set; }
        public string Word { get; set; }
        public string TimeStamp { get; set; }

        public SearchWordList(int Id, string Word, string TimeStamp)
        {
            this.Id = Id;
            this.Word = Word;
            this.TimeStamp = TimeStamp;
        }
    }

    public partial class MainPage : ContentPage
    {
        public static string DbPath { get; }
            = System.IO.Path.Combine(
                Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
                , "SQLiteDataBase.db");

        public static ObservableCollection<SearchWordList> WordListData { get; set; }
            = new ObservableCollection<SearchWordList>();
        public MainPage()
        {
            InitializeComponent();
            // ListViewの初期化
            WordListData = new ObservableCollection<SearchWordList>();
            // データベース初期設定
            using (var db = new SQLite.SQLiteConnection(DbPath))
            {   // テーブル作成
                db.CreateTable<SearchWord>();
                // データ取得
                foreach (var row in db.Table<SearchWord>())
                {
                    WordListData.Add(new SearchWordList(row.Id, row.Word, row.TimeStamp.ToString()));
                }
            }

            // ListViewにデータソースをセット
            wordList.ItemsSource = WordListData;
        }

        async void OnButtonSearch(object sender, EventArgs e)
        {
            if (Device.RuntimePlatform == Device.Android)
            {
                try
                {
                    IGeolocator locator = CrossGeolocator.Current;
                    // 1. 50mの精度に指定
                    // locator.DesiredAccuracy = 50;
                    Position position = await locator.GetPositionAsync();

                    string result = position.Latitude.ToString() + ',' + position.Longitude.ToString();
                    string search_text = ((Button)sender).Text;

                    await Launcher.OpenAsync("geo:" + result + "?q=" + search_text);
                }
                catch
                {
                    await DisplayAlert("確認", "位置情報をオンにしてください。", "OK");
                }

            }
        }

        private void OnButtonDelete(object sender, EventArgs e)
        {
            SearchWordList remove_list = (SearchWordList)((Button)sender).CommandParameter;
            using (var db = new SQLite.SQLiteConnection(MainPage.DbPath))
            {
                db.Delete<SearchWord>(remove_list.Id);
            }
            WordListData.Remove(remove_list);
        }

        private void NextPage(object sender, EventArgs e)
        {
            Navigation.PushAsync(new SubPage());
        }


    }
}
```

#検索ワード登録画面
```xml:SubPage.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"
             x:Class="AppGoogleMap.SubPage"
             Title="検索ワード登録画面">
    <StackLayout>
        <Label Text="登録する検索ワードを入力してください。" />
        <Entry x:Name="insertText" Placeholder="登録内容" MaxLength="100" />
        <Button Text="登録" Clicked="OnButtonInsert" />
    </StackLayout>
</ContentPage>
```

```c#:SubPage.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace AppGoogleMap
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class SubPage : ContentPage
    {
        public SubPage()
        {
            InitializeComponent();
        }
        
        private void OnButtonInsert(object sender, EventArgs e)
        {
            // 登録制限
            if (insertText.Text == null || insertText.Text == "")
            {
                // 登録成功アラート
                DisplayAlert("登録失敗", "入力文字が空です。", "OK");
                return;
            }

            DateTime temp_now = DateTime.Now;
            String temp_word = insertText.Text;
            // データ追加
            using (var db = new SQLite.SQLiteConnection(MainPage.DbPath))
            {   
                db.Insert(new SearchWord() {TimeStamp = temp_now, Word = temp_word});
                foreach (var row in db.Query<SearchWord>("select Id, Word, TimeStamp from SearchWord where ROWID = last_insert_rowid();"))
                {
                    MainPage.WordListData.Add(new SearchWordList(row.Id, row.Word, row.TimeStamp.ToString()));
                }
            }
            // 登録成功アラート
            DisplayAlert("登録成功", insertText.Text + "を登録しました。", "OK");
        }

    }
}
```

#参考サイト
【Xamarin Forms で SQLite を使う】
https://rksoftware.hatenablog.com/entry/2018/01/01/135248

【Xamarin.Formsで画面遷移を実装】
https://rainbow-engine.com/xamarin-page-transition/

【Xamarin.Forms ListViewのデータソース更新時にUIを更新させる方法】
https://qiita.com/chooyan_eng/items/35594efea1bd5109b0f4

【Xamarinでデバイスの許可をアプリのダイアログで行う方法】
※位置情報権限を許可するために参考にした
https://intellectual-curiosity.tokyo/2020/01/09/xamarin%E3%81%A7%E3%83%87%E3%83%90%E3%82%A4%E3%82%B9%E3%81%AE%E8%A8%B1%E5%8F%AF%E3%82%92%E3%82%A2%E3%83%97%E3%83%AA%E3%81%AE%E3%83%80%E3%82%A4%E3%82%A2%E3%83%AD%E3%82%B0%E3%81%A7%E8%A1%8C%E3%81%86/

#この記事のコード

コードはGitHubで公開しております。

https://github.com/sunn-sudo/AppGoogleMap