XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書
顯示具有 Map 標籤的文章。 顯示所有文章
顯示具有 Map 標籤的文章。 顯示所有文章

2016/07/12

Xamarin.Forms 建置 Android 專案,發生 java.lang.OutOfMemoryError 錯誤

建置 Android 專案,發生 java.lang.OutOfMemoryError 錯誤

有時候,當建置 Xamarin.Android 專案的時候,只有發現一個錯誤,那就是java.lang.OutOfMemoryError, 可是,整個專案都檢查過了,沒有任何問題,此時,請參考這篇文章,進行問題修復。
嚴重性 程式碼 說明 專案 檔案 行 隱藏項目狀態 錯誤 java.lang.OutOfMemoryError. Consider increasing the value of $(JavaMaximumHeapSize). Java ran out of memory while executing 'java.exe -jar "C:\Program Files (x86)\Android\android-sdk\build-tools\23.0.1\lib\dx.jar" --no-strict --dex --output=obj\Debug\android\bin obj\Debug\android\bin\classes "C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\MonoAndroid\v5.0\mono.android.jar" obj\Debug_libraryprojects\FormsViewGroup\library_project_imports\formsviewgroup.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.Animated.Vector.Drawable\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.Design\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.v4\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.v4\23.3.0.0\embedded\libs\internal_impl-23.3.0.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.v7.AppCompat\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.v7.CardView\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.v7.MediaRouter\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.v7.MediaRouter\23.3.0.0\embedded\libs\internal_impl-23.3.0.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.v7.RecyclerView\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.Android.Support.Vector.Drawable\23.3.0.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.GooglePlayServices.AppIndexing\8.4.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.GooglePlayServices.Base\8.4.0\embedded\classes.jar C:\Users\vulca\AppData\Local\Xamarin\Xamarin.GooglePlayServices.Basement\8.4.0\embedded\classes.jar' WorkingWithGestures.Android
java.lang.OutOfMemoryError

解決方式

請使用滑鼠右擊 Android 專案,點選 屬性 項目,當出現該專案的屬性標籤頁次,請接著點選 Android Options > Advanced,此時,您會看到 Advanced Android Build Settings 這個項目裡有個 Java Max Heap Size,請在該項目的底下文字輸入盒輸入 1G。並且,請重新編一看看,您的問題應該就已經解決了。
Android專案屬性JavaMaxHeap

Xamarin.Forms 進階應用 Font Awesome

進階應用 Font Awesome

對於想要在 Xamarin.Forms 專案內,想要用 Font Awesome 字體,您需要分別設定 Android 與 iOS 類型專案,使其可以使用 Font Awesome 字體圖示。

下載 Font Awesome 字型檔案與安裝

先打開瀏覽器,連線https://github.com/vulcanlee/Xamarin.Forms.StepByStep/tree/master/Font%20Awesome 會看到 Font Awesome 字型檔案 fontawesome.ttf ,點擊該連結,下載 Font Awesome的檔案到您本機磁碟機某個目錄上。
使用檔案總管,將剛剛下載的這個 fontawesome.ttf 檔案,拖拉到 Android 專案內的 Assets 資料夾內。
fontawesome.ttf在Assets內
使用檔案總管,將這個 fontawesome.ttf 檔案,拖拉到 iOS 專案內的 Resources 資料夾內。
fontawesome.ttf在Resources內

原生專案的修正

想要在 Android 與 iOS 系統內使用 Font Awesome 提供的字體圖示,做法並不一樣,底下分別就兩個平台的做法進行說明。

Android 專案部分

請在 Android 專案內,建立一個名為 Renderers 資料夾,並且在此資料夾內,建立一個類別檔案AwesomeRenderer.cs。該類別檔案的內容如下所示:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Xamarin.Forms.Platform.Android;
using Xamarin.Forms;
using XFGestures.Droid;
using Android.Graphics;
using XFGestures.Droid.Renders;

[assembly: ExportRenderer(typeof(Label), typeof(AwesomeLabelRenderer))]
[assembly: ExportRenderer(typeof(Xamarin.Forms.Button), typeof(AwesomeButtonRenderer))]
namespace XFGestures.Droid.Renders
{
    public class AwesomeLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            AwesomeUtil.CheckAndSetTypeFace(Control);
        }
    }

    public class AwesomeButtonRenderer : ButtonRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.Button> e)
        {
            base.OnElementChanged(e);

            AwesomeUtil.CheckAndSetTypeFace(Control);
        }
    }

    internal static class AwesomeUtil
    {
        public static void CheckAndSetTypeFace(TextView view)
        {
            if (view.Text.Length == 0) return;
            var text = view.Text;
            if (text.Length > 1 || text[0] < 0xf000)
            {
                return;
            }

            var font = Typeface.CreateFromAsset(Xamarin.Forms.Forms.Context.ApplicationContext.Assets, "fontawesome.ttf");
            view.Typeface = font;
        }
    }
}

iOS 專案部分

在 iOS 專案中,使用滑鼠右擊 Info.plist 檔案,在彈出功能表中,選擇 開啟方式,再出現 開啟方式 - Info.plist 對話窗後,請選擇 XML(文字)編輯器,接著,點擊 確定 按鈕。
此時,Visual Studio 會開啟 XML 編輯器,請參考下兩圖,加入底下 XML 宣告到 Info.plist 檔案內。
    <key>UIAppFonts</key>
    <array>
      <string>fontawesome.ttf</string>
    </array>  </dict>
  • Info.plist尚未修改前的內容截圖
    Info.plist尚未修改前
  • Info.plist尚未修改後的內容截圖
    Info.plist尚未修改後

如何使用 Font Awesome

在 XAML 內

  • 請使用 Label 控制項,並且在 Text 屬性上使用 &#xf000; 這樣的表示式這個圖示字體http://fontawesome.io/icon/glass/
  • 另外,請使用 FontFamily="FontAwesome" 來表示需要使用 Font Awesome 的圖示字體
<Label Text="&#xf000;"
           FontSize="60"
           FontFamily="FontAwesome"
           Margin="0,0,40,0"
           />

在 C# 內

string FAGlass = "\uf000";

如何查詢有哪些可用的 Font Awesome 圖示字體

2016/07/09

Xamarin.Forms 地圖功能

地圖功能

在這一章節,要來說明如何開發地圖相關應用的 Xamarin.Forms 應用程式;另外,也會提到完全不使用 XAML 宣告定義的方式,而是採用 C# 程式語言直接進行 Xamarin.Forms 的程式開發方法。當您看完這一章節之後,可以判斷出,使用 XAML 與 純C# 這兩種開發方式,哪種比較方便與好用。
在這個範例程式內,將會透過四個子標籤頁面,來展示 Xamarin.Forms.Maps 這個 NuGet 套件,如何實作出地圖應用程式常用的各項功能。
  • 地圖縮放 子標籤頁面
    提供最下方的按鈕,可以切換不同地圖種類、可以透過手勢或者控制項來進行地圖縮放
  • 位置標示 子標籤頁面
    可以動態插入位置標示控制項,且可以顯示更多詳細資訊、如何讓地圖重新回到預設中心點
  • 定位反查 子標籤頁面
    可以透過 Xamarin.Forms.Maps 提供的方法
    • 進行地址->座標查詢
    • 進行座標->地址的反查
  • 外部地圖 子標籤頁面
    展示如何透過手機內建的地圖程式,來顯示相關資訊

建立標籤式的樣板式頁面方案

  1. 首先,開啟您的 Visual Studio 2015
  2. 接著透過 Visual Studio 2015 功能表,選擇這些項目 檔案 > 新增 > 專案 準備新增一個專案。
  3. 接著,Visual Studio 2015 會顯示 新增專案 對話窗,請在這個對話窗上,進行選擇 Visual C# >Cross-Platform > Blank Xaml App (Xamarin.Forms Portable)
  4. 接著,在最下方的 名稱 文字輸入盒處,輸入 XFMap 這個名稱,最後使用滑鼠右擊右下方的 確定 按鈕。
  5. 當專案建立到一半,若您的開發環境還沒有建置與設定完成 Mac 電腦與 Xamarin Studio for Mac 系統,此時會看到 Xamarin Mac Agent Instructions 對話窗出現,這個對話窗是要提醒您進行與 Mac 電腦連線設定,這是因為,您無法在 Windows 作業系統進行 iOS 相關應用程式的建立與設計工作,而是需要透過 Mac 電腦安裝的 XCode 來協助您完成這些 iOS 應用程式的建立工作。不過,這不影響您繼續開發 Xamarin.Forms 的應用程式,只不過此時,您無法編譯與執行 iOS 的應用程式。
  6. 接著會看到 新的通用Windows專案 對話視窗,此時,您只需要按下 確定 按鈕即可,此時,專案精靈會繼續完成相關平台的專案建立工作。
  7. 最後,整個新的 Xamarin.Forms 專案就建立完成了。

Xamarin.Forms.Maps 元件

若您想要在 Xamarin.Forms 應用程式中,開發具有地圖功能能力,您需要在方案內的所有專案,都需要安裝Xamarin.Forms.Maps 這個套件。
  1. 在方案總管的 XFMap 節點,使用滑鼠右鍵點選,選擇 管理方案的 NuGet 套件... 選項。當 NuGet - 解決方案 內容顯示出來後,請切換到 瀏覽 標籤頁次,接著在搜尋文字輸入盒內,輸入要尋找的套件名稱: Xamarin.Forms.Maps。當搜尋結果出來之後,第一個搜尋結果內容就是 Xamarin.Forms.Maps,點選這個套件,接著,在右方選擇要安裝這個套件到該方案內的所有專案中,最後,點選 安裝 按鈕,進行把 NuGet 套件 Xamarin.Forms.Maps 安裝到所有的專案內。
    安裝Xamarin.FormsMaps套件

原生專案的 Maps 初始化

請分別在各個行動裝置平台專案,進行地圖套件的初始化工作。

Android 的 Maps 初始化

請在Android專案 (XFMap.Droid)內,打開 MainActivity.cs 檔案,找到 OnCreate 方法,在 Xamarin.Forms.Init 方法後,加入 Xamarin.FormsMaps.Init(this, bundle);。最後修正完成的結果如下列表。
        protected override void OnCreate(Bundle bundle)
        {
            TabLayoutResource = Resource.Layout.Tabbar;
            ToolbarResource = Resource.Layout.Toolbar;

            base.OnCreate(bundle);

            global::Xamarin.Forms.Forms.Init(this, bundle);
            global::Xamarin.FormsMaps.Init(this, bundle);

            LoadApplication(new App());
        }

iOS 的 Maps 初始化

請在iOS專案 (XFMap.iOS)內,打開 AppDelegate.cs 檔案,找到 FinishedLaunching 方法,在 Xamarin.Forms.Init 方法後,加入 Xamarin.FormsMaps.Init();。最後修正完成的結果如下列表。
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
    {
        //
        // This method is invoked when the application has loaded and is ready to run. In this 
        // method you should instantiate the window, load the UI into it and then make the window
        // visible.
        //
        // You have 17 seconds to return from this method, or iOS will terminate your application.
        //
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();
            Xamarin.FormsMaps.Init();
            LoadApplication(new App());

            return base.FinishedLaunching(app, options);
        }
    }

複製所需要用到的圖片資源

這個練習專案需要用到一些圖片檔案,因此,您需要進行將這些圖片檔案複製到適當的位置,這些檔案位於https://github.com/vulcanlee/Xamarin.Forms.StepByStep/tree/master/Images/XFMap

Android 平台

當圖片檔案下載下來之後,請使用檔案總管全選這些圖片檔案,並且拖拉到 Android 專案 XFMap.Droid >Resources > drawable 目錄內,如下圖所示:
當這些圖片檔案拖拉到 Android 專案內的 drawable 資料夾內,這些圖片檔案的建置動作必須是AndroidResource
複製圖片檔案到Android專案內

iOS 平台

請使用檔案總管全選這些圖片檔案,並且拖拉到 iOS 專案 XFMap.iOS > Resources 目錄內,如下圖所示:
當這些圖片檔案拖拉到 Android 專案內的 Resources 資料夾內,這些圖片檔案的建置動作必須是BundleResource
複製圖片檔案到iOS專案內

開始進行 核心PCL 頁面設計

App.xaml.cs

在 App 類別的建構式內,使用底下程式碼置換。
在 App 建構式內,首先建立一個 TabbedPage 頁面,接著,新增四個子標籤頁面,這四個子標籤頁面都是ContentPage 類型,且每個子標籤頁面,都有設定專屬的圖示圖片,這將會顯示在應用程式頁面上。

App.xaml.cs

        public App()
        {
            InitializeComponent();

            //MainPage = new XFMap.MainPage();

            var tabs = new TabbedPage();

            // demonstrates the map control with zooming and map-types
            tabs.Children.Add(new MapPage { Title = "地圖縮放", Icon = "glyphish_74_location.png" });

            // demonstrates the map control with zooming and map-types
            tabs.Children.Add(new PinPage { Title = "位置標示", Icon = "glyphish_07_map_marker.png" });

            // demonstrates the Geocoder class
            tabs.Children.Add(new GeocoderPage { Title = "定位反查", Icon = "glyphish_13_target.png" });

            // opens the platform's native Map app
            tabs.Children.Add(new MapAppPage { Title = "外部地圖", Icon = "glyphish_103_map.png" });

            MainPage = tabs;
        }

MapPage.cs

  1. 建立 MapPage
    使用滑鼠右擊核心PCL專案,XFMap,在彈出視窗內,點選 加入 > 新的項目,當出現 加入新項目 - XFMap 對話視窗後,點選 Visual C# > Cross-Platform > Forms ContentPages, 最後,在下方名稱欄位的文字輸入盒內輸入:MapPage
  2. 修改程式碼內容 將底下的程式碼,全部置換剛剛產生的 MapPage.cs
  3. MapPage 頁面定義了一個地圖控制項 Map;並且透過了該地圖控制項提供的 MoveToRegion 方法,將地圖預設顯示的中心點與範圍調整成您想要的參數,在這裡,將會使用某個座標點的方圓500公尺。
  4. 另外,滑動尺標 (Slider) 控制項,也可以提供地圖要顯示的大小與範圍。
  5. 在此,也定義了三個按鈕,可以切換地圖要顯示的類型:接到模式、衛星模式、兩者混合模式。

MapPage.cs

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection.Emit;
using System.Text;

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

namespace XFMap
{
    public class MapPage : ContentPage
    {
        Map map;
        public MapPage()
        {
            map = new Map
            {
                //IsShowingUser = true,
                HeightRequest = 100,
                WidthRequest = 960,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            // You can use MapSpan.FromCenterAndRadius 
            //map.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (37, -122), Distance.FromMiles (0.3)));
            // or create a new MapSpan object directly
            //map.MoveToRegion(new MapSpan(new Position(25.0436439, 121.525664), 0.01, 0.01));
            map.MoveToRegion(MapSpan.FromCenterAndRadius(new Position(25.0436439, 121.525664), Distance.FromMeters(500)));

            // add the slider
            var slider = new Slider(1, 18, 1);
            slider.ValueChanged += (sender, e) => {
                var zoomLevel = e.NewValue; // between 1 and 18
                var latlongdegrees = 360 / (Math.Pow(2, zoomLevel));
                Debug.WriteLine(zoomLevel + " -> " + latlongdegrees);
                if (map.VisibleRegion != null)
                    map.MoveToRegion(new MapSpan(map.VisibleRegion.Center, latlongdegrees, latlongdegrees));
            };


            // create map style buttons
            var street = new Button { Text = "街道" };
            var hybrid = new Button { Text = "混合" };
            var satellite = new Button { Text = "衛星" };
            street.Clicked += HandleClicked;
            hybrid.Clicked += HandleClicked;
            satellite.Clicked += HandleClicked;
            var segments = new StackLayout
            {
                Spacing = 30,
                HorizontalOptions = LayoutOptions.CenterAndExpand,
                Orientation = StackOrientation.Horizontal,
                Children = { street, hybrid, satellite }
            };


            // put the page together
            var stack = new StackLayout { Spacing = 0 };
            stack.Children.Add(map);
            stack.Children.Add(slider);
            stack.Children.Add(segments);
            Content = stack;


            // for debugging output only
            map.PropertyChanged += (sender, e) => {
                Debug.WriteLine(e.PropertyName + " just changed!");
                if (e.PropertyName == "VisibleRegion" && map.VisibleRegion != null)
                    CalculateBoundingCoordinates(map.VisibleRegion);
            };
        }

        void HandleClicked(object sender, EventArgs e)
        {
            var b = sender as Button;
            switch (b.Text)
            {
                case "街道":
                    map.MapType = MapType.Street;
                    break;
                case "混合":
                    map.MapType = MapType.Hybrid;
                    break;
                case "衛星":
                    map.MapType = MapType.Satellite;
                    break;
            }
        }


        /// <summary>
        /// In response to this forum question http://forums.xamarin.com/discussion/22493/maps-visibleregion-bounds
        /// Useful if you need to send the bounds to a web service or otherwise calculate what
        /// pins might need to be drawn inside the currently visible viewport.
        /// </summary>
        static void CalculateBoundingCoordinates(MapSpan region)
        {
            // WARNING: I haven't tested the correctness of this exhaustively!
            var center = region.Center;
            var halfheightDegrees = region.LatitudeDegrees / 2;
            var halfwidthDegrees = region.LongitudeDegrees / 2;

            var left = center.Longitude - halfwidthDegrees;
            var right = center.Longitude + halfwidthDegrees;
            var top = center.Latitude + halfheightDegrees;
            var bottom = center.Latitude - halfheightDegrees;

            // Adjust for Internation Date Line (+/- 180 degrees longitude)
            if (left < -180) left = 180 + (180 + left);
            if (right > 180) right = (right - 180) - 180;
            // I don't wrap around north or south; I don't think the map control allows this anyway

            Debug.WriteLine("Bounding box:");
            Debug.WriteLine("                    " + top);
            Debug.WriteLine("  " + left + "                " + right);
            Debug.WriteLine("                    " + bottom);
        }
    }
}

PinPage.cs

  1. 建立 PinPage
    使用滑鼠右擊核心PCL專案,XFMap,在彈出視窗內,點選 加入 > 新的項目,當出現 加入新項目 - XFMap 對話視窗後,點選 Visual C# > Cross-Platform > Forms ContentPages, 最後,在下方名稱欄位的文字輸入盒內輸入:PinPage
  2. 修改程式碼內容 將底下的程式碼,全部置換剛剛產生的 PinPage.cs
  3. PinPage 頁面展示說明如何使用位置標示大頭針,進行在地圖上標示某些座標值。想要使用這項功能,您首先需要使用類別 Position 產生要標示的座標點,接著使用類別 Pin 定義,當點選了這個位置標示大頭針後,要顯示的內容。

PinPage.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;

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

namespace XFMap
{
    public class PinPage : ContentPage
    {
        Map map;

        public PinPage()
        {
            map = new Map
            {
                IsShowingUser = true,
                HeightRequest = 100,
                WidthRequest = 960,
                VerticalOptions = LayoutOptions.FillAndExpand
            };

            map.MoveToRegion(MapSpan.FromCenterAndRadius(
                new Position(36.9628066, -122.0194722), Distance.FromMiles(3))); // Santa Cruz golf course

            var position = new Position(36.9628066, -122.0194722); // Latitude, Longitude
            var pin = new Pin
            {
                Type = PinType.Place,
                Position = position,
                Label = "Santa Cruz",
                Address = "custom detail info"
            };
            map.Pins.Add(pin);


            // create buttons
            var morePins = new Button { Text = "加入更多 位置標示" };
            morePins.Clicked += (sender, e) => {
                map.Pins.Add(new Pin
                {
                    Position = new Position(36.9641949, -122.0177232),
                    Label = "Boardwalk"
                });
                map.Pins.Add(new Pin
                {
                    Position = new Position(36.9571571, -122.0173544),
                    Label = "Wharf"
                });
                map.MoveToRegion(MapSpan.FromCenterAndRadius(
                    new Position(36.9628066, -122.0194722), Distance.FromMiles(1.5)));

            };
            var reLocate = new Button { Text = "重新定位" };
            reLocate.Clicked += (sender, e) => {
                map.MoveToRegion(MapSpan.FromCenterAndRadius(
                    new Position(36.9628066, -122.0194722), Distance.FromMiles(3)));
            };
            var buttons = new StackLayout
            {
                Orientation = StackOrientation.Horizontal,
                Children = {
                    morePins, reLocate
                }
            };

            // put the page together
            Content = new StackLayout
            {
                Spacing = 0,
                Children = {
                    map,
                    buttons
                }
            };
        }
    }
}

GeocoderPage.cs

  1. 建立 GeocoderPage
    使用滑鼠右擊核心PCL專案,XFMap,在彈出視窗內,點選 加入 > 新的項目,當出現 加入新項目 - XFMap 對話視窗後,點選 Visual C# > Cross-Platform > Forms ContentPages, 最後,在下方名稱欄位的文字輸入盒內輸入:GeocoderPage
  2. 修改程式碼內容 將底下的程式碼,全部置換剛剛產生的 GeocoderPage.cs
  3. GeocoderPage 頁面展示如何使用 Geocoder 這個類別所產生的物件,進行
    • 由座標點來查詢實際的地址 (使用 GetAddressesForPositionAsync 方法)
    • 由地址來查詢實際的座標點 (使用 GetPositionsForAddressAsync 方法)

GeocoderPage.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;

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

namespace XFMap
{
    public class GeocoderPage : ContentPage
    {
        Geocoder geoCoder;
        Label l = new Label();

        public GeocoderPage()
        {
            geoCoder = new Geocoder();

            var b1 = new Button { Text = "Reverse geocode '37.808, -122.432'" };
            b1.Clicked += async (sender, e) => {
                var fortMasonPosition = new Position(37.8044866, -122.4324132);
                var possibleAddresses = await geoCoder.GetAddressesForPositionAsync(fortMasonPosition);
                foreach (var a in possibleAddresses)
                {
                    l.Text += a + "\n";
                }
            };

            var b2 = new Button { Text = "Geocode '394 Pacific Ave'" };
            b2.Clicked += async (sender, e) => {
                var xamarinAddress = "394 Pacific Ave, San Francisco, California";
                var approximateLocation = await geoCoder.GetPositionsForAddressAsync(xamarinAddress);
                foreach (var p in approximateLocation)
                {
                    l.Text += p.Latitude + ", " + p.Longitude + "\n";
                }
            };

            Content = new StackLayout
            {
                Padding = new Thickness(0, 20, 0, 0),
                Children = {
                    b2,
                    b1,
                    l
                }
            };
        }
    }
}

MapAppPage.cs

  1. 建立 MapAppPage
    使用滑鼠右擊核心PCL專案,XFMap,在彈出視窗內,點選 加入 > 新的項目,當出現 加入新項目 - XFMap 對話視窗後,點選 Visual C# > Cross-Platform > Forms ContentPages, 最後,在下方名稱欄位的文字輸入盒內輸入:MapAppPage
  2. 修改程式碼內容 將底下的程式碼,全部置換剛剛產生的 MapAppPage.cs
  3. MapAppPage 頁面定義了一個標籤(Lable)和兩個按鈕(Button),之後,都放置到版面配置控制項StackLayout 內,且以垂直方式排列。
  4. 這兩個按鈕按下之後,首先,使用 Device.OS 這個功能,查看現在Xamarin.Forms 應用程式是在哪個行動作業系統上執行,接著,會根據不同平台專屬的產生不同 URI,接著,透過 Device.OpenUri 方法,開啟這些 URI。在不同的作業系統下,這些專屬 URI 要如何定義與描述,這個問題需要您參考個行動作業系統的 SDK 文件,不過,原則上,可以參考這個範例程式,大致可以了解如何使用。

MapAppPage.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Emit;
using System.Text;

using Xamarin.Forms;

namespace XFMap
{
    public class MapAppPage : ContentPage
    {
        // WARNING: when adding latitude/longitude values be careful of localization.
        // European (and other countries) use a comma as the separator, which will break the request

        public MapAppPage()
        {
            var l = new Label
            {
                Text = "底下兩個按鈕按下之後,將會離開當前的應用程式,並且打開原生系統內建的地圖應用程式"
            };

            var openLocation = new Button
            {
                Text = "使用原生系統內建地圖程式,顯示指定座標"
            };
            openLocation.Clicked += (sender, e) => {

                if (Device.OS == TargetPlatform.iOS)
                {
                    //https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html
                    Device.OpenUri(new Uri("http://maps.apple.com/?q=394+Pacific+Ave+San+Francisco+CA"));
                }
                else if (Device.OS == TargetPlatform.Android)
                {
                    // opens the Maps app directly
                    Device.OpenUri(new Uri("geo:0,0?q=394+Pacific+Ave+San+Francisco+CA"));

                }
                else if (Device.OS == TargetPlatform.Windows)
                {
                    Device.OpenUri(new Uri("bingmaps:?where=394 Pacific Ave San Francisco CA"));
                }
            };

            var openDirections = new Button
            {
                Text = "使用原生系統內建地圖程式,提供導航資訊"
            };
            openDirections.Clicked += (sender, e) => {

                if (Device.OS == TargetPlatform.iOS)
                {
                    //https://developer.apple.com/library/ios/featuredarticles/iPhoneURLScheme_Reference/MapLinks/MapLinks.html
                    Device.OpenUri(new Uri("http://maps.apple.com/?daddr=San+Francisco,+CA&saddr=cupertino"));

                }
                else if (Device.OS == TargetPlatform.Android)
                {
                    // opens the 'task chooser' so the user can pick Maps, Chrome or other mapping app
                    Device.OpenUri(new Uri("http://maps.google.com/?daddr=San+Francisco,+CA&saddr=Mountain+View"));

                }
                else if (Device.OS == TargetPlatform.Windows)
                {
                    Device.OpenUri(new Uri("bingmaps:?rtp=adr.394 Pacific Ave San Francisco CA~adr.One Microsoft Way Redmond WA 98052"));
                }
            };
            Content = new StackLayout
            {
                Padding = new Thickness(5, 20, 5, 0),
                HorizontalOptions = LayoutOptions.Fill,
                Children = {
                    l,
                    openLocation,
                    openDirections
                }
            };
        }
    }
}

執行結果

底下分別來查看在不同平台下,執行這個應用程式所看到的成果是甚麼?

Android 執行結果

請在方案總管內,滑鼠右擊 XFMap.Droid 專案,選擇 設定為起始專案,接著按下 F5 開始執行。
當您可以正常在模擬器執行這個範例程式,應該會出現如下圖的畫面,此時您需要點選 Update 按鈕,進行Google Play Services 升級動作。
Android平台需要升級GooglePlayServices
GooglePlayServiceUpdate
於更新完成後,停止該專案執行,並且再度按下 F5 重新執行;此時,很不幸的,您看到 Visual Studio 出現例外異常訊息。
com.google.android.geo.API_KEY
會產生這樣的錯誤訊息,這是因為,如同上圖文字的說明,您需要取得 Google Maps Android API 金鑰,並且設定到 AndroidManifest.xml 檔案內等相關設定工作,才能夠正常在 Android 系統下使用地圖功能。

取得簽署檔案 SHA1 指紋

在進行取得 Google Maps API 金鑰前,您需要得到您的簽署檔案 (keystore) 的 SHA1 指紋資訊,這在哩,將會把除錯簽屬檔案(Keystore)用的SHA1指紋資訊,綁定到 Google Maps API 內。
預設來說,Xamarin.Android 的預設除錯用的簽署檔案是位於(Windows 作業系統下)
C:\Users\[USERNAME]\AppData\Local\Xamarin\Mono for Android\debug.keystore
而要得到簽屬金鑰的SHA1指紋資訊,需要使用到底下工具(Windows 作業系統下)
C:\Program Files (x86)\Java\jdk[VERSION]\bin\keytool.exe
因此,在這裡,假設使用者帳號為 vulcan,Java JDK 版本為 1.7.0.55,則需要取得除錯的簽屬檔案指紋指令下法為:
"C:\Program Files (x86)\Java\jdk1.7.0_55\bin\keytool.exe" -list -v -keystore "C:\Users\vulca\AppData\Local\Xamarin\Mono for Android\debug.keystore" -alias androiddebugkey -storepass android -keypass android
當下達上述指令,會產生出底下結果,其中,SHA1: 3C:D6:80:32:35:70:72:58:43:74:81:E6:F4:AB:C3:5E:65:55:DD:85,這個資訊就是簽署檔案的SHA1指紋,請先複製這個指紋資訊,等下會用到。
別名名稱: androiddebugkey
建立日期: 2016/5/5
項目類型: PrivateKeyEntry
憑證鏈長度: 1
憑證 [1]:
擁有者: CN=Android Debug, O=Android, C=US
發出者: CN=Android Debug, O=Android, C=US
序號: 257c88bf
有效期自: Thu May 05 02:33:12 CST 2016 到: Sat Apr 28 02:33:12 CST 2046
憑證指紋:
     MD5:  7A:AB:DC:5C:FF:6B:2C:6A:6C:8A:70:3A:EF:D8:97:22
     SHA1: 3C:D6:80:32:35:70:72:58:43:74:81:E6:F4:AB:C3:5E:65:55:DD:85
     SHA256: 92:5D:17:FF:FF:47:F1:03:55:0B:D3:AC:0A:01:00:0A:77:E9:84:F1:1A:9B:6F:51:52:1C:8A:3F:B7:04:D1:2F
     簽章演算法名稱: SHA256withRSA
     版本: 3

擴充套件: 

#1: ObjectId: 2.5.29.14 Criticality=false
SubjectKeyIdentifier [
KeyIdentifier [
0000: A7 65 E1 07 00 D0 51 1F   B4 FE 68 FF 7E 3A A5 30  .e....Q...h..:.0
0010: 4E 85 F6 4D                                        N..M
]
]

申請 Google Maps Android API

  1. 首先,要來 Google API Console https://console.developers.google.com/iam-admin/projects 頁面,建立一個新的專案。
  2. 點選 + 新建專案 ,在這裡建立一個新的 XFMap 專案,並且點選 建立 按鈕。
    建立新GoogleAPI專案
  3. 若專案還沒有建立完成,右上角的鐘圖示會有處理中的動畫呈現
    正在建立專案中
  4. 當專案建立完成後,會進入到 Google API 申請頁面,這時,請確認右上角的正在使用的專案,是您所要申請的API的專案;在這裡,點選 Google Maps Android API 這個連結,準備申請 Google Maps for Android 要用到的金鑰。
    GoogleAPI申請首頁
  5. 當進入到 Google Maps Android API 申請首頁時候,您需要先啟動這項服務,請點選 啟用 按鈕;當這個按鈕顯示為 停用 文字之後,就表示這個服務已經啟用了。請接著點選 前往「憑證」 這個按鈕,請參考下下圖。
    GoogleMapsAndroidAPI
    前往憑證
  6. 當進入到 將憑證新增至您的專案 頁面,請設定:
    您目前使用哪個 API? > Google Maps Android API
    API的呼叫來源為何? > Android裝置
    最後,請點選 我需要那些憑證? 按鈕
    將憑證新增至您的專案
  7. 請在第二個步驟 建立API金鑰 內的名稱欄位,輸入您想要的金鑰名稱(用於備註之用),在這裡輸入了Android 金鑰 XFMap。請接著點選 + 新增 「套件名稱和指紋」 按鈕。
    此時,需要在套件名稱內填入 Android 應用程式的套件名稱,這裡將會填入:com.miniasp.xfmap
    在 SHA-1 憑證指紋 欄位內,請填入上面取得道的簽署檔案 SHA1 指紋
    最後,請點選 建立 API 金鑰 按鈕
    建立API金鑰
    限制您的Android應用程式的使用量
  8. 這時,已經進入到 將憑證新增至您的專案 的最後一個步驟,取得您的憑證。請複製螢幕畫面上的 API 金鑰,並且點選 完成 按鈕。
    取得您的憑證

新增應用程式可以使用的能力

  • 滑鼠雙擊專案 XFMap.Droid下的 Properties 這個節點
  • 請點選 Android Manifest,並請底下的 Configuration properties 的 Required permissions 清單內,選擇底下的權限。
    • AccessCoarseLocation
    • AccessFineLocation
    • AccessLocationExtraCommands
    • AccessMockLocation
    • AccessNetworkState
    • AccessWifiState
    • Internet
    RequiredPermissions

修正 XFMap.Droid 專案,加入 API 金鑰

  • 滑鼠展開專案 XFMap.Droid下的 Properties 這個節點
  • 找到 AndroidManifest.xml 檔案,將其打開
  • 加入 meta-data 節點資料到 application 節點內,如下面列表
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.miniasp.xfmap" android:installLocation="auto">
    <uses-sdk android:minSdkVersion="15" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
    <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <application android:label="$safeprojectname$">
        <meta-data android:name="com.google.android.maps.v2.API_KEY" android:value="AIzaSyBdQkIzAPJdPDCwXEnDUbayTE571-sW-sw" />
        <meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />
    </application>
</manifest>

設定應用程式的套件名稱

  • 請將前面在申請 Google Maps Android API 時候,用到的 套件名稱 複製下來。
  • 滑鼠雙擊專案 XFMap.Droid下的 Properties 這個節點
  • 請點選 Android Manifest,並請在 Package name 欄位內,輸入 com.miniasp.xfmap
PackageName

實際執行畫面

請再度重新執行,這個時候應用程式可以順利正常運行,螢幕截圖如下:
  • 在 地圖縮放 標籤子頁面內,會顯示預設的中心點的500公尺的地圖 AndroidXFMap1
  • 在 地圖縮放 子標籤頁面內,點選最下方的按鈕,可以切換不同地圖種類 AndroidXFMap2
  • 在 位置標示 子標籤頁面內,可以透過位置標示圖示,標示出任何座標的地點資訊 AndroidXFMap3
  • 在 定位反查 子標籤頁面,可以透過內建方法,進行地址->座標查詢或者進行座標->地址的反查 AndroidXFMap4
  • 在 外部地圖 子標籤頁面,展示如何透過手機內建的地圖程式,來顯示相關資訊 AndroidXFMap5
    AndroidXFMap5
    AndroidXFMap5

佈署注意事項

iOS 執行結果

請在方案總管內,滑鼠右擊 XFMap.iOS 專案,選擇 設定為起始專案,接著按下 F5 開始執行。

佈署注意事項

參考