shuhelohelo’s blog

Xamarin.Forms多めです.

Xamarin.FormsでMapを使うチュートリアルメモ

www.youtube.com

Xamarin.Forms Essentialでは外部のMapアプリ(Google Mapなど)を呼び出している.

この動画で使用しているのはアプリケーション内でMapの機能を利用する方法.

こんな感じでアプリ内で地図を表示させることができる.

ピンも立てられるようだ.

f:id:shuhelohelo:20191231195115p:plain

Xamarin.Forms.Mapのインストール

f:id:shuhelohelo:20191231195405p:plain

すべてのプロジェクトに追加する.

iOS

AppDelegate.cs

        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            Xamarin.FormsMaps.Init();//これ

            global::Xamarin.Forms.Forms.Init();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }

Android

MainActivity.cs

        protected override void OnCreate(Bundle savedInstanceState)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(savedInstanceState);
            Xamarin.FormsMaps.Init(this, savedInstanceState);//これ

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());
        }

マニフェストの編集

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.mapapp">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" />
    <application android:label="MapApp.Android">
      <!--これ-->
      <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR_API_KEY"/>
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

このAPIキーはGoogle MapのAPIキーか?

Info.plist

<dict>
...
    <key>NSLocationAlwaysUsageDescription</key>
    <string>Can we use your location at all times?</string>
    <key>NSLocationWhenInUseUsageDescription</key>
    <strin>Can we use your location when your app is being used?</strin>
    <key>NSLocationAlwaysAndWhenInUseUsageDesription</key>
    <string>Can we use your location at all times?</string>
</dict>

Google Map APIキーを取得する

Google Map APIキーの取得についてはこちらの記事を参考にしてください.

shuhelohelo.hatenablog.com

JSONからクラスを生成する

サンプルデータのPlaces.jsonファイルを追加する.

Places.jsonファイルのBuild ActionをEmbedded resourceにする.

f:id:shuhelohelo:20191231213929p:plain

Places.json内のデータをコピーしてクリップボードに入れた状態で,Visual StudioのメニューからEdit -> Paste Special -> Paste JSON as Classesを選ぶと,JSONからクラスを生成してくれる.

f:id:shuhelohelo:20191231210811p:plain

ルートオブジェクトの名前がRootobjectになっているので,それをPlacesに変更しておく.

    public class Places
    {
        public object[] html_attributions { get; set; }
        public string next_page_token { get; set; }
        public Result[] results { get; set; }
        public string status { get; set; }
    }

あとPlaceクラスを追加する.

    public class Place
    {
        public string PlaceName { get; set; }
        public string Address { get; set; }
        public string Icon { get; set; }
        public string Distance { get; set; }
        public string OpenNow { get; set; }
        public Position Position { get; set; }
        public Location Location { get; set; }
    }

Places.jsonのデータを読み込んでMapコントロールに表示する

    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();

            //Mapの描画が終わるのを待っている?
            Task.Delay(2000);
            UpdateMap();
        }


        List<Place> placesList = new List<Place>();

        private void UpdateMap()
        {
            try
            {
                //Embedded resourceのPlaces.jsonを読み込む.文字列として
                var assembly = IntrospectionExtensions.GetTypeInfo(typeof(MainPage)).Assembly;
                Stream stream = assembly.GetManifestResourceStream("MapApp.Places.json");
                string text = string.Empty;
                using (var reader = new StreamReader(stream))
                {
                    text = reader.ReadToEnd();
                }

                //文字列のJsonをPlacesオブジェクトに変換する
                var resultObject = JsonConvert.DeserializeObject<Places>(text);

                //場所の情報PlaceをListに追加する.
                foreach (var place in resultObject.results)
                {
                    placesList.Add(new Place
                    {
                        PlaceName = place.name,
                        Address = place.vicinity,
                        Location = place.geometry.location,
                        Position = new Position(place.geometry.location.lat, place.geometry.location.lng),
                        //Icon = place.icon,
                        //Distance = $"{GetDistance(lat1, lon1, place.geometry.location.lat, place.geometry.location.lng, DistanceUnit.Kiliometers).ToString("N2")}km",
                        //OpenNow = GetOpenHours(place?.opening_hours?.open_now)
                    });
                }

                MyMap.ItemsSource = placesList;
                //PlacesListView.ItemsSource = placesList;
                //var loc = await Xamarin.Essentials.Geolocation.GetLocationAsync();
                MyMap.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(47.6370891183, -122.123736172), Distance.FromKilometers(100)));

            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }
        }
    }

いざ実行

APIキーもセットして実行してみると,以下のようなエラーが出ました. 「Java.Lang.NoClassDefFoundError: 'Failed resolution of: Lorg/apache/http/ProtocolVersion;'」

f:id:shuhelohelo:20191231232211p:plain

これは以下の情報によると,この項目をAndroidManifest.xml内のの中に入れることで回避できるとのこと.

     <uses-library android:name="org.apache.http.legacy" android:required="false" />

これでAndroidManifest.xmlは以下のようになります. 「INETERNET」の許可も追加しました.

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.mapapp" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="26" android:targetSdkVersion="29" />
    <application android:label="MapApp.Android">
    <!--これ重要-->
        <uses-library android:name="org.apache.http.legacy" android:required="false" />
        <!--これ-->
        <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="YOUR_API_KEY" />
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
</manifest>

表示されました. f:id:shuhelohelo:20191231232756p:plain

写経ソースコード

github.com