Xamarin.Forms 地图初始化和配置

Download Sample下载示例

Map 控件在每个平台上使用本机地图控件。 这为用户提供快速、熟悉的地图体验,但意味着需要执行一些配置步骤遵守每个平台 API 要求。

地图初始化

Map 控件由 Xamarin.Forms.Maps NuGet 包提供,该包应添加到解决方案中的每个项目中。

安装 Xamarin.Forms.Maps NuGet 包后,必须在每个平台项目中进行初始化。

在 iOS 上,应通过先后调用 Xamarin.FormsMaps.Init 方法和 Xamarin.Forms.Forms.Init 方法来在 AppDelegate.cs 中执行初始化

Xamarin.FormsMaps.Init();

在 Android 上,应通过先后调用 Xamarin.FormsMaps.Init 方法和 Xamarin.Forms.Forms.Init 方法来在 MainActivity.cs 中执行初始化

Xamarin.FormsMaps.Init(this, savedInstanceState);

在通用 Windows 平台 (UWP) 上,这应该在 MainPage.xaml.cs 中进行,方法是从 MainPage 构造函数中调用 Xamarin.FormsMaps.Init 方法:

Xamarin.FormsMaps.Init("INSERT_AUTHENTICATION_TOKEN_HERE");

有关 UWP 所需的身份验证令牌的信息,请参阅通用 Windows 平台

添加 NuGet 包并在每个应用程序内调用初始化方法后,可以在共享代码项目中使用 Xamarin.Forms.Maps API。

平台配置

在 Android 和通用 Windows 平台 (UWP) 上需要进行额外配置,才能显示地图。 此外,在 iOS、Android 和 UWP 上,访问用户的位置需要事先向应用程序授予位置权限。

iOS

在 iOS 上显示地图并与之互动不需要任何额外配置。 不过,要访问位置服务,必须在 Info.plist 中设置以下键

要支持 iOS 11 及更早版本,可以包含所有三个键:NSLocationWhenInUseUsageDescriptionNSLocationAlwaysAndWhenInUseUsageDescriptionNSLocationAlwaysUsageDescription

Info.plist 中这些密钥的 XML 表示形式如下所示。 应更新 string 值,以反映应用程序如何使用位置信息:

<key>NSLocationAlwaysUsageDescription</key>
<string>Can we use your location at all times?</string>
<key>NSLocationWhenInUseUsageDescription</key>
<string>Can we use your location when your application is being used?</string>
<key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
<string>Can we use your location at all times?</string>

在编辑 Info.plist 文件时,也可以在“源”视图中添加 Info.plist 条目

Info.plist for iOS 8

当应用程序尝试访问用户位置时,系统会显示提示,请求访问:

Screenshot of location permission request on iOS

Android

在 Android 上显示地图并与之交互的配置过程如下:

  1. 获取 Google Maps API 密钥并将其添加到清单。
  2. 在清单中指定 Google Play 服务版本号。
  3. 在清单中指定 Apache HTTP 旧库的要求。
  4. [可选]在清单中指定 WRITE_EXTERNAL_STORAGE 权限。
  5. [可选]在清单中指定位置权限。
  6. [可选] 请求 MainActivity 类中的运行时位置权限。

有关正确配置的清单文件的示例,请参阅示例应用程序中的 AndroidManifest.xml

获取 Google Maps API 密钥

若要在 Android 上使用 Google Maps API,必须生成 API 密钥。 为此,请按照获取 Google Maps API 密钥中的说明进行操作。

获得 API 密钥后,必须将其添加到 Properties/AndroidManifest.xml 文件的 <application> 元素中:

<application ...>
    <meta-data android:name="com.google.android.geo.API_KEY" android:value="PASTE-YOUR-API-KEY-HERE" />
</application>

这会将 API 密钥嵌入清单中。 如果没有有效的 API 密钥,则 Map 控件将显示空白网格。

注意

com.google.android.geo.API_KEY 是 API 密钥的推荐元数据名称。 为了向后兼容,可以使用 com.google.android.maps.v2.API_KEY 元数据名称,但只允许对 Android 地图 API v2 进行身份验证。

为了使 APK 访问 Google Maps,务必包含用于对 APK 进行签名的每个密钥存储(调试和发布)的 SHA-1 指纹和包名称。 例如,如果你使用一台计算机进行调试,将另一台计算机用于生成发布 APK,则应包含第一台计算机的调试密钥存储的 SHA-1 证书指纹,以及第二台计算机的发布密钥存储的 SHA-1 证书指纹。 如果应用的包名称更改,也要记得编辑密钥凭据。 请参阅获取 Google Maps API 密钥

指定 Google Play 服务版本号

在 AndroidManifest.xml<application> 元素中添加以下声明:

<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version" />

这会将应用程序编译时使用的 Google Play 服务版本嵌入到清单中。

指定 Apache HTTP 旧库的要求

如果 Xamarin.Forms 应用程序面向 API 28 或更高版本,则必须在 AndroidManifest.xml<application> 元素中添加以下声明:

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

这会告知应用程序使用 Apache Http 客户端库,该库已从 Android 9 的 bootclasspath 中移除。

指定 WRITE_EXTERNAL_STORAGE 权限

如果应用程序面向 API 22 或更低版本,可能需要在清单中添加 WRITE_EXTERNAL_STORAGE 权限,作为 <manifest> 元素的子元素:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

如果应用程序面向 API 23 或更高版本,则不需要这样做。

指定位置权限

如果应用程序需要访问用户的位置,必须在清单中添加 ACCESS_COARSE_LOCATION 或/和 ACCESS_FINE_LOCATION 权限(作为 <manifest> 元素的子元素)以请求权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.myapp">
  ...
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
</manifest>

ACCESS_COARSE_LOCATION 权限允许 API 使用 WiFi 或移动数据(或两者)确定设备的位置。 ACCESS_FINE_LOCATION 权限允许 API 使用全球定位系统 (GPS)、WiFi 或移动数据确定尽可能精确的位置。

或者,可以使用清单编辑器添加以下权限来启用这些权限:

  • AccessCoarseLocation
  • AccessFineLocation

以下屏幕截图中显示了这些内容:

Required permissions for Android

请求运行时位置权限

如果应用程序面向 API 23 或更高版本,并需要访问用户的位置,则必须检查在运行时是否拥有所需的权限,如果没有,则请求获得权限。 这可通过以下操作实现:

  1. MainActivity 类中,添加以下字段:

    const int RequestLocationId = 0;
    
    readonly string[] LocationPermissions =
    {
        Manifest.Permission.AccessCoarseLocation,
        Manifest.Permission.AccessFineLocation
    };
    
  2. MainActivity 类中,添加以下 OnStart 替代:

    protected override void OnStart()
    {
        base.OnStart();
    
        if ((int)Build.VERSION.SdkInt >= 23)
        {
            if (CheckSelfPermission(Manifest.Permission.AccessFineLocation) != Permission.Granted)
            {
                RequestPermissions(LocationPermissions, RequestLocationId);
            }
            else
            {
                // Permissions already granted - display a message.
            }
        }
    }
    

    如果应用程序面向 API 23 或更高版本,该代码会对 AccessFineLocation 权限执行运行时权限检查。 如果未授予权限,则通过调用 RequestPermissions 方法发出权限请求。

  3. MainActivity 类中,添加以下 OnRequestPermissionsResult 替代:

    public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
    {
        if (requestCode == RequestLocationId)
        {
            if ((grantResults.Length == 1) && (grantResults[0] == (int)Permission.Granted))
                // Permissions granted - display a message.
            else
                // Permissions denied - display a message.
        }
        else
        {
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
    

    此替代处理权限请求的结果。

此代码的总体效果是,当应用程序请求用户的位置时,会显示以下请求权限的对话框:

Screenshot of location permission request on Android

通用 Windows 平台

在 UWP 上,必须先对应用程序进行身份验证,然后才能显示地图并使用地图服务。 若要对应用程序进行身份验证,必须指定地图身份验证密钥。 有关详细信息,请参阅请求地图身份验证密钥。 然后,应在 FormsMaps.Init("AUTHORIZATION_TOKEN") 方法调用中指定身份验证令牌,以通过必应地图对应用程序进行身份验证。

注意

在 UWP 上,要使用地理编码等地图服务,还必须将 MapService.ServiceToken 属性设置为身份验证密钥值。 这可以通过以下代码行来实现:Windows.Services.Maps.MapService.ServiceToken = "INSERT_AUTH_TOKEN_HERE";

此外,如果应用程序需要访问用户的位置,则必须在包清单中启用位置功能。 这可通过以下操作实现:

  1. 解决方案资源管理器中,双击 package.appxmanifest 并选择功能选项卡。

  2. 功能列表中,选中位置框。 这会将 location 设备功能添加到包清单文件。

    <Capabilities>
      <!-- DeviceCapability elements must follow Capability elements (if present) -->
      <DeviceCapability Name="location"/>
    </Capabilities>
    

发行版本

UWP 版本生成使用 .NET 本机编译将应用程序直接编译为本机代码。 但是,这样做的结果是 UWP 上的 Map 控件的呈现器可能会链接到可执行文件之外。 通过使用 App.xaml.cs 中 Forms.Init 方法的 UWP 特定重载,可以解决这个问题

var assembliesToInclude = new [] { typeof(Xamarin.Forms.Maps.UWP.MapRenderer).GetTypeInfo().Assembly };
Xamarin.Forms.Forms.Init(e, assembliesToInclude);

此代码将 Xamarin.Forms.Maps.UWP.MapRenderer 类所在的程序集传递给 Forms.Init 方法。 这样可以确保程序集不会被 .NET 本机编译进程链接到可执行文件之外。

重要

未能执行此操作将导致在运行版本生成时不显示 Map 控件。