ぴーさんログ

だいたいXamarin.Formsのブログ

【Xamarin.Forms 2.2.0(プレビュー)】CarouselView

Xamarin.Forms 2.2.0 から CarouselView クラスが追加されます。

CarouselView は従来の CarouselPage を置き換える物で、CarouselPageは将来的に非推奨となります。

サンプル

基本的な使い方は ListView に似ています。しかし、DataTemplateの中身を CellではなくView にする必要がある点に注意が必要です。

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="XFApp20.MyPage">
    <ContentPage.Content>
        <CarouselView HorizontalOptions="Center"
                      VerticalOptions="Center"
                      HeightRequest="150"
                      WidthRequest="150"
                      ItemsSource="{Binding}">
            <!-- DataTemplateの中身はView派生クラス -->
            <CarouselView.ItemTemplate>
                <DataTemplate>
                    <StackLayout>
                        <BoxView HorizontalOptions="FillAndExpand"
                                 VerticalOptions="FillAndExpand"
                                 Color="{Binding Color}" />
                        <Label HorizontalOptions="Center"
                               Text="{Binding Text}" />
                    </StackLayout>
                </DataTemplate>
            </CarouselView.ItemTemplate>
        </CarouselView>
    </ContentPage.Content>
</ContentPage>

コードビハインド

public MyPage()
{
    InitializeComponent();

    BindingContext = new ViewModel[] {
        new ViewModel {
            Color = Color.Red,
            Text = "Red",
        },
        new ViewModel {
            Color = Color.Blue,
            Text = "Blue",
        },
        new ViewModel {
            Color = Color.Green,
            Text = "Green",
        },
        new ViewModel {
            Color = Color.Purple,
            Text = "Purple",
        },
        new ViewModel {
            Color = Color.Silver,
            Text = "Silver",
        },
        new ViewModel {
            Color = Color.Pink,
            Text = "Pink",
        },
    };
}
public class ViewModel
{
    public Color Color { get; set; }
    public string Text { get; set; }
}

ItemTemplateのセッティングをC#で書くとこのようになります。

carouselView.ItemTemplate = new DataTemplate(typeof(CustomView));

// または

carouselView.ItemTemplate = new DataTemplate(() => {
    var boxView = new BoxView(){
        VerticalOptions = LayoutOptions.FillAndExpand,
        HorizontalOptions = LayoutOptions.FillAndExpand,
    };
    boxView.SetBinding(BoxView.ColorProperty, "Color");
    var label = new Label{
        HorizontalOptions = LayoutOptions.Center,
    };
    label.SetBinding(Label.TextProperty, "Text");
    return new StackLayout {
        Children = {
            boxView,
            label,
        },
    };
});

実行結果

f:id:ticktack623:20160413091209g:plain:w250

【Xamarin.Forms】BindableProperty.Create() non-generic版のコードスニペット

以前、BindablePropertyを楽に作るコードスニペットを公開しました。

その中で使っていたBindableProperty.Create()のgeneric版がobsolete化したので、新しくnon-generic版のスニペットを公開します。

詳細な登録手順はgeneric版の記事を合わせてご参照ください。

Xamarin Studio用

Preferences > テキストエディタ > コード テンプレート から追加します。

Visual Studio

「BindableProperty.snippet」の名前で保存。 「C:\Users\ ユーザー名 \Documents\ VisualStudioのバージョン \Code Snippets\Visual C#\My Code Snippets」にファイルをコピー。

ツール > コードスニペットマネージャー から追加します。

【Xamarin.Forms 2.2.0(プレビュー)】Marginプロパティ

Xamarin.Forms 2.2.0 から View クラスに Margin プロパティが追加されます。 複雑なレイアウトが今までより平易に書けるようになりますね。

(ずっと来ないのでパフォーマンス的に厳しいのかと思ってまいした)

サンプル

public App()
{
    MainPage = new ContentPage {
        Content = new StackLayout {
            Orientation = StackOrientation.Horizontal,
            HorizontalOptions = LayoutOptions.Center,
            VerticalOptions = LayoutOptions.Center,
            BackgroundColor = Color.Gray,
            Spacing = 0,
            Children = {
                new Label {
                    Text = "◯",
                    Margin = new Thickness(10, 10, 10, 10),
                    BackgroundColor = Color.Green,
                },
                new Label {
                    Text = "△",
                    Margin = new Thickness(10, 30, 10, 10),
                    BackgroundColor = Color.Blue,
                },
                new Label {
                    Text = "□",
                    Margin = new Thickness(20, 20, 20, 20),
                    BackgroundColor = Color.Red,
                },
            },
        }
    };
}

f:id:ticktack623:20160409090910j:plain

これはこれで思った通りにレイアウトするには少し慣れが必要かも。

Plugins for Xamarinを作ろう!

前置き

Build 2016での発表から全ての開発者がVisual StudioでXamarinを利用できるようになり、アプリのみならずクロスプラットフォームなライブラリ開発も行いやすくなりました。

という訳で今回はPlugins for XamarinのテンプレートからNuGetパッケージを作るまでの流れを解説します。

Plugins for Xamarin って何?

Xamarin.iOS, Xamarin.Android, Windows Phone などの固有機能を共通のインターフェースで利用できるようにデザインされたライブラリ群です。 代表的なものにバッテリー、ストレージ、位置情報などを利用するためのプラグインがあります。

Plugins for Xamarinを 使いこなす 方法はBuild Insiderで田淵さん(@ytabuchi)連載している記事を参照してください。

Plugins for Xamarinを使いこなすには?(ファイルシステム編) - Build Insider 他

環境準備

  • Visual Studio 2015 Update 2
  • Xamarin SDK
  • Plugin For Xamarin用のプロジェクトテンプレート
  • Windows 10推奨(UWPプロジェクトを含むので)

(筆者の環境はWindows 10 & Visual Studio Enterprise 2015 with Update 2です)

まずはVisual StudioとXamarinをインストールしてiOSAndroidアプリがビルドできる環境を整えてください。 (先駆者達の環境構築記事を見ながら頑張りましょう)

次にGitHubにあるPlugin For Xamarinのリポジトリを開きます。

xamarin/plugins: Plugins for Xamarin

「Tools to get Started」以下のリンクからプロジェクトテンプレートを入手してください。 (一応こちらにもリンクを張っておきます。)

プロジェクトを作ろう!

準備が整ったら新規プロジェクトを作成しましょう。 テンプレート名は「Plugin for Xamarin」です。("plugin"で絞り込みをかけると楽)

f:id:ticktack623:20160407233019j:plain

ソリューション名は"Battery"や"Geolocator"のような 機能名 にするのがお約束です。

続いて生成されたプロジェクトを眺めてみましょう。

f:id:ticktack623:20160407233839j:plain

プロジェクトが沢山あってびっくりしますが、以下の3種類があると押さえておけば問題ありません。

  • Plugin.機能名 (PCL)
  • Plugin.機能名.Abstractions (PCL)
  • Plugin.機能名.プラットフォーム名 * n個

Plugin.機能名.Abstractions (PCL)

インターフェースやenumを定義します。 出力は Plugin.機能名.Abstractions.dll となります。

Plugin.機能名 (PCL)

PCLプロジェクト用のダミー実装を定義します。「なんのこっちゃ?」と思うかもしれませんが、実はこれが無いとPCLプロジェクトでPluginを参照するときに困ります。 出力は Plugin.機能名.dll となります。

Plugin.機能名.プラットフォーム名

各プラットフォーム用の実装を定義します。 出力は Plugin.機能名.dll となります。

プラグインを実装しよう!

この記事ではサンプルとして、アプリケーションのバージョンを取得するプラグインを実装します。 (not assembly version)

Plugin.機能名.Abstractions (PCL) プロジェクト

Plugin.機能名.AbstractionsプロジェクトにはInterfaceやenum、Event等を定義します。

テンプレートから生成された IAwesomeFeature.cs にコードを追加します。

using System;

namespace Plugin.AwesomeFeature.Abstractions
{
  /// <summary>
  /// Interface for AwesomeFeature
  /// </summary>
  public interface IAwesomeFeature
  {
        // 追加したコード
        string GetAppVersion();
  }
}

Plugin.機能名 (PCL) プロジェクト

Plugin.機能名プロジェクトにはPCLプロジェクトから参照するためのダミー実装となります。 Cross機能名.cs だけが生成されており、プラグインをsingletonなAPIラッパーとする限りコードを変更する必要はありません。

using Plugin.AwesomeFeature.Abstractions;
using System;

namespace Plugin.AwesomeFeature
{
  /// <summary>
  /// Cross platform AwesomeFeature implemenations
  /// </summary>
  public class CrossAwesomeFeature
  {
    static Lazy<IAwesomeFeature> Implementation = new Lazy<IAwesomeFeature>(() => CreateAwesomeFeature(), System.Threading.LazyThreadSafetyMode.PublicationOnly);

    /// <summary>
    /// Current settings to use
    /// </summary>
    public static IAwesomeFeature Current
    {
      get
      {
        var ret = Implementation.Value;
        if (ret == null)
        {
          throw NotImplementedInReferenceAssembly();
        }
        return ret;
      }
    }

    static IAwesomeFeature CreateAwesomeFeature()
    {
#if PORTABLE
        return null;
#else
        return new AwesomeFeatureImplementation();
#endif
    }

    internal static Exception NotImplementedInReferenceAssembly()
    {
      return new NotImplementedException("This functionality is not implemented in the portable version of this assembly.  You should reference the NuGet package from your main application project in order to reference the platform-specific implementation.");
    }
  }
}

Plugins for Xamarinは Plugin.機能名.Cross機能名.Current でアクセスするようデザインされており、 Currentプロパティにアクセスした際にプラットフォーム実装が無ければ例外となります。 (具体的には、対象プラットフォームにプラグイン実装が提供されていない場合、PCLプロジェクトにプラグインのNuGetパッケージを追加したのにプラットフォーム側のプロジェクトでパッケージ追加を忘れた場合)

ちなみに、各プラットフォームプロジェクトはこの Cross機能名.cs をリンク参照しています。

Plugin.機能名.プラットフォーム名 プロジェクト

今回はAndroid版にだけ機能を実装することにします。

各プラットフォームのプロジェクトには 機能名Implementation.cs ファイルが生成されているので、ここにコードを追加します。

using Plugin.AwesomeFeature.Abstractions;
using System;

namespace Plugin.AwesomeFeature
{
  /// <summary>
  /// Implementation for Feature
  /// </summary>
  public class AwesomeFeatureImplementation : IAwesomeFeature
  {
        // インターフェースの実装を追加
        public string GetAppVersion()
        {
            var context = Android.App.Application.Context;
            return context.PackageManager.GetPackageInfo(context.PackageName, 0).VersionName;
        }
    }
}

NuGetパッケージを作ろう!

ソリューションを右クリックから新規項目を追加。

「Plugins for Xamarin NuSpec」を追加、名前はソリューションと同じく機能名にします。 ("nuspec"でフィルタリングすると楽)

f:id:ticktack623:20160408141828j:plain

f:id:ticktack623:20160408142526j:plain

今回は動作確認のため、NuGet.orgに公開しないローカルなNuGetパッケージとして作成します。nuspecやNuGetパッケージの作り方に関しては、動作確認に必要な最低限の解説に留めます。(私にとってnugetは複雑で難しいので)

nuspecファイルを編集します。

  • <licenseUrl /><projectUrl/>
    • 必須項目なので架空のUrlで埋めておきます。
  • <files></files>

準備が整ったら、各プロジェクトをビルドしてパッケージマネージャコンソールで次のコマンドを実行します。

nuget pack

成功するとソリューションフォルダの直下にNuGetパッケージファイル(nupkg)が出力されます。

f:id:ticktack623:20160408145421j:plain

f:id:ticktack623:20160408145727j:plain

NuGetパッケージの作成はMacでも可能ですが、Windows版でしか使えない機能($id$等のマクロ、MS Buildに依存してる)があったりするのでWindowsで行った方が何かと便利でしょう。

NuGetパッケージを使おう!

それでは出来上がったNuGetパッケージをローカルとして動作確認してみましょう。

まず、任意のフォルダにnupkgファイルを配置します。(例では C:\PackageSource )

続いて動作確認用のプロジェクト(「Blank App(Xamarin.Forms Portable)」)を新規作成します。

ツール > NuGetパッケージ マネージャー > パッケージ マネージャー設定 を開きます。

f:id:ticktack623:20160408152047j:plain

「パッケージ ソース」に新しいパッケージソースを追加修正します。

  • 名前: 任意
  • ソース: nupkgファイルを配置したフォルダ

PCLプロジェクトとAndroidプロジェクトにNuGetパッケージを追加します。この際、パッケージソースを先ほど作ったローカルフォルダに変更してください。

f:id:ticktack623:20160408152759j:plain

PCLプロジェクトのAppクラスを動作確認用に修正します。

public App()
{
    var button = new Button
    {
        Text = "Show dialog"
    };
    var page = new ContentPage
    {
        Content = new StackLayout
        {
            VerticalOptions = LayoutOptions.Center,
            Children = {
                button,
            }
        }
    };

    button.Clicked += (s, e) =>
    {
        page.DisplayAlert("App version" ,
            Plugin.AwesomeFeature.CrossAwesomeFeature.Current.GetAppVersion(),
            "OK");
    };

    MainPage = page;
}

早速Androidを実行してみます。

f:id:ticktack623:20160408161146j:plain:w220 f:id:ticktack623:20160408161204j:plain:w220

ばっちりAndroid固有の実装が動いています!

それでは固有実装を用意していないUWP版を実行するとどうなるでしょう?

f:id:ticktack623:20160408154647j:plain:w400 f:id:ticktack623:20160408154751j:plain

エラー無くビルドできましたが、ボタンをクリックするとNotImplementedExceptionが発生しました。 これはPCLプロジェクトからダミー実装のdllを引き継いだためですね。

Bait and Switch

さて、PCLプロジェクトの出力であるdll(ここではApp3.dll)はAndroid、UWPの両方に全く同じものが同梱されています。 そして、PluginのdllはAndroidとUWPでそれぞれ違うものが同梱されています。(Android:固有実装、UWP:ダミー実装)

これの何が凄いかというと、PCLプロジェクトが「ビルド時に参照していたdll」と「実行時に参照するdll」をすり替えたにもかかわらず、メソッド呼び出しができている点です。 これは「Bait and Switch」と呼ばれるトリックです。(assembly name、version、class構造が一致していればいいらしい?)

「Bait and Switch」実現のテンプレート化こそがPlugins for Xamarinの真骨頂であると言えます。

Androidアプリに固有実装dllが同梱されている点も重要だと思うんですが、これはリンカーの仕様なんでしょうか? (同名dllが自プロジェクトと外部プロジェクト(PCL)で参照されていたら、自プロジェクトの方を優先?)

最後に

NuGetパッケージの仕様など、説明しきれない部分もありましたがPlugins for Xamarinがどんなものか何となく理解していただければ幸いです。

ぜひともクールなプラグインを作ってNuGet公開にチャレンジしてください。

↓の要求事項に応えればPlugins for Xamarinに仲間入りできるみたいですよ?

Requirements of a Plugin

  • Open source on GitHub
  • Documentation on GitHub's README file
  • Name: "FEATURE_NAME Plugin for Xamarin and Windows"
  • Namespace: Plugin.FEATURE_NAME
  • App-store friendly OSS license (we like MIT)
  • No dependency on Xamarin.Forms

リンク

xamarin/plugins: Plugins for Xamarin (GitHub)

The Bait and Switch PCL Trick

Build 2016とXamarin.Forms

Build 2016で 超!エキサイティン!! な発表がありました、なんとVisual Studioの全エディション(Community含む)にXamarinライセンスが無料で付くようになりました。

さらに、諸々のSDKオープンソースになります。

In addition to these important steps, we are announcing today our commitment to open source the Xamarin SDKs for Android, iOS, and Mac under the MIT license in the coming months. This includes native API bindings and the basic command-line tools necessary to develop mobile apps. It also includes our popular cross-platform native UI toolkit, Xamarin.Forms.

Xamarin for Everyone | Xamarin Blog

これには Xamarin.Forms も含まれるので、前回の記事で言う所の「変化球:オープンソースになる」が正解とあいなりました。

実際にオープンソースになるのは少し先のようですが楽しみですね!

Xamarin買収でXamarin.Formsが今後どうなるか考えてみる

結構今更なタイミングではありますがXamarin.Formsが今後どうなるかつらつらと考えてみました。

総評としては「UIデザイナが追加されたら様子見してた人達も突入してもOK」といったところでしょうか。

思いつくパターン

強化される

MSの圧倒的リソースが投入され、UIデザイナが追加されたり、パフォーマンスが向上したりする。 当然、UWP対応も正式版となりさらに強化される。

なくなる

MS主導で別の事にリソースを集中させる等の理由で開発停止。 (代わりに新プロジェクトが始まるといいね)

現状維持

今まで通りのペースで開発続行。UIデザイナを作る余裕は無いんじゃないかな。 (デザイナ? ねぇよ、んなモン)

変化球:オープンソースになる

2通り思いつく。

無いだろなーと思うパターン

UWPに置き換えられる

UWPのコードをiOSAndroidで動くようにするパターン。 Xamarin.Formsに相当する層をイチから作ることになる、そんなのやる意味無いでしょ。

【Xamarin.Forms】Effectsを使ったトリック 1

Xamarin.Forms 2.1.0 がリリースされたのでEffectを利用したトリックを解説します。

今回は Xamarin.Forms.Maps.Map (にアタッチしたBehavior)に地図のタップイベントを生やすというものです。

同様のことがカスタムMapクラス&カスタムRendererで実現できますが、これから紹介する手法の方が再利用製に優れます。 (既存のカスタムMapライブラリに対しても適用できたり) 一方で、Effectを使う都合上インスタンス生成に介入できない点には注意が必要です。

地図を使うための準備(AndroidManifestやAPIキー、info.plist等)については割愛します。 (大体はここに書いてある→『 Map Control - Xamarin 』)

作るもの

  • MapExtensionBehavior
    • Mapクラスの拡張機能を提供するクラス、タップイベントはここに生やす
  • MapEffect (iOS/Android)
    • Effectの実装クラス、ネイティブのイベントをハンドルする

コード

今回のサンプルはSharedプロジェクト構成です。 PCLの場合は System.Runtime.CompilerServices.InternalsVisibleToAttribute を使うなどする必要があります。多分。

MapExtensionBehavior

Sharedプロジェクトに追加します。

using System;
using Xamarin.Forms;
using Xamarin.Forms.Maps;

namespace EffectsTric
{
    public class MapExtensionBehavior : Behavior<Map>
    {
        private readonly string effectId = "MapExtensions.MapEffect";
        private Effect effect;
        private IMapEffect mapEffect;

        protected override void OnAttachedTo(Map bindable)
        {
            base.OnAttachedTo(bindable);

            if(bindable as Map == null)
                return;

            effect = Effect.Resolve(effectId);
            mapEffect = effect as IMapEffect;
            // ネイティブのEffect実装から上がったイベントを右から左します
            mapEffect.MapTapped += (sender, e) => 
                MapTapped?.Invoke(this, new MapTappedEventArgs(e.Position));

            bindable.Effects.Add (effect);
        }

        protected override void OnDetachingFrom(Map bindable)
        {
            bindable?.Effects?.Remove(effect);
            effect = null;
            mapEffect = null;

            base.OnDetachingFrom(bindable);
        }
        
        public event EventHandler<MapTappedEventArgs> MapTapped;

        // ネイティブのEffect実装からイベントを上げてもらうためのインターフェース
        internal interface IMapEffect
        {
            event EventHandler<MapTappedEventArgs> MapTapped;
        }
    }
    
    // タップ座標を受け取るためのEventArgs
    [Serializable]
    public sealed class MapTappedEventArgs : EventArgs
    {
        public Position Position
        {
            get;
        }

        public MapTappedEventArgs(Position position)
        {
            Position = position;
        }
    }
}

MapEffect (iOS)

iOSプロジェクトに追加します。

using System;
using System.Linq;
using Xamarin.Forms;
using Xamarin.Forms.Maps;
using Xamarin.Forms.Platform.iOS;
using MapKit;
using CoreLocation;
using UIKit;
using EffectsTric;

[assembly: ResolutionGroupName ("MapExtensions")]
[assembly: ExportEffect (typeof (EffectsTric.iOS.MapEffect), "MapEffect")]

namespace EffectsTric.iOS
{
    public class MapEffect : PlatformEffect, MapExtensionBehavior.IMapEffect
    {
        private MKMapView mapView;
        private MapExtensionBehavior behavior;
        private UITapGestureRecognizer tapGesture;

       #region implemented abstract members of Effect

        protected override void OnAttached()
        {
            mapView = Control as MKMapView;
            if(mapView == null)
                return;

            behavior = (Element as Map)?.Behaviors?.OfType<MapExtensionBehavior>()?.FirstOrDefault();
            if(behavior == null)
                return;
            
            // MKMapViewにGestureRecognizerを追加
            tapGesture = new UITapGestureRecognizer((recognizer) => 
            {
                // タップ座標 → 緯度経度 → Position型に変換してイベント発火
                var point = recognizer.LocationInView(mapView);
                var coordinate = mapView.ConvertPoint(point, mapView);
                MapTapped?.Invoke(this, new MapTappedEventArgs(new Position(
                    coordinate.Latitude, coordinate.Longitude)));
            })
            {
                NumberOfTapsRequired = 1,

            };
            mapView.AddGestureRecognizer(tapGesture);
        }

        protected override void OnDetached()
        {
            mapView.RemoveGestureRecognizer(tapGesture);
            tapGesture.Dispose();
            tapGesture = null;
            mapView = null;
            behavior = null;
        }

       #endregion

        public event EventHandler<MapTappedEventArgs> MapTapped;
    }
}

MapEffect (Android)

Androidプロジェクトに追加します。

using System;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Gms.Maps;
using Xamarin.Forms.Maps;
using System.Linq;
using EffectsTric;

[assembly: ResolutionGroupName ("MapExtensions")]
[assembly: ExportEffect (typeof (EffectsTric.Droid.MapEffect), "MapEffect")]

namespace EffectsTric.Droid
{
    public class MapEffect : PlatformEffect, MapExtensionBehavior.IMapEffect
    {
        public event EventHandler<MapTappedEventArgs> MapTapped;

        private GoogleMap googleMap;
        private MapExtensionBehavior behavior;

       #region implemented abstract members of Effect

        protected override void OnAttached()
        {
            var mapView = Control as MapView;
            if(mapView == null)
                return;

            behavior = (Element as Map)?.Behaviors?.OfType<MapExtensionBehavior>()?.FirstOrDefault();
            if(behavior == null)
                return;
            
            // GoogleMapインスタンスを得る
            var callback = new OnMapReadyCallback();
            callback.OnMapReadyAction = (gMap) => {
                googleMap = gMap;
                googleMap.MapClick += GoogleMap_MapClick;
            };
            mapView.GetMapAsync(callback);
        }

        protected override void OnDetached()
        {
            if(googleMap != null)
            {
                googleMap.MapClick -= GoogleMap_MapClick;
                googleMap = null;
            }
        }

       #endregion

        void GoogleMap_MapClick (object sender, GoogleMap.MapClickEventArgs e)
        {
            // GoogleMapのクリックイベントから緯度経度をPosition型に変換してイベント発火
            MapTapped?.Invoke(this, new MapTappedEventArgs(
                new Position(e.Point.Latitude,
                    e.Point.Longitude)));
        }

        // GoogleMapインスタンスを得るのに使う
        class OnMapReadyCallback : Java.Lang.Object, IOnMapReadyCallback
        {
            public Action<GoogleMap> OnMapReadyAction;

            public void OnMapReady(GoogleMap googleMap)
            {
                OnMapReadyAction?.Invoke(googleMap);
            }
        }
    }
}

動作サンプル

使用側のサンプルコード

public App()
{
    // タップ座標確認用のラベル
    var label = new Label {
        HorizontalOptions = LayoutOptions.Center,
        VerticalOptions = LayoutOptions.Center,
        InputTransparent = true,
        BackgroundColor = Color.Gray.MultiplyAlpha(0.3),
        TextColor = Color.Black,
    };
    
    // Map機能を拡張するBehavior
    var behavior = new MapExtensionBehavior();
    behavior.MapTapped += (sender, e) => {
        label.Text = e.Position.Latitude + ", " + e.Position.Longitude;
    };
    
    // ここがカスタムMapになってもBehaviorは使い回せる
    var map = new Map {
        IsShowingUser = true,
        Behaviors = {
            behavior
        },
    };

    // 国会議事堂付近
    var position = new Position(35.675889, 139.744972);
    map.MoveToRegion(new MapSpan(
        position, 0.8, 0.8));

    MainPage = new ContentPage {
        Content = new Grid {
            Children = {
                map,
                label,
            },
        },
    };
}

Androidエミュレータで地図使えるようにするのはちょっと大変なので動画はiOSだけ。

f:id:ticktack623:20160309201635g:plain