Xamarin.Forms 地图初始化和配置
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 及更高版本
NSLocationWhenInUseUsageDescription
- 为了在使用应用程序时使用位置服务NSLocationAlwaysAndWhenInUseUsageDescription
– 为了始终使用位置服务
- iOS 10 及更早版本
NSLocationWhenInUseUsageDescription
- 为了在使用应用程序时使用位置服务NSLocationAlwaysUsageDescription
– 为了始终使用位置服务
要支持 iOS 11 及更早版本,可以包含所有三个键:NSLocationWhenInUseUsageDescription
、NSLocationAlwaysAndWhenInUseUsageDescription
和 NSLocationAlwaysUsageDescription
。
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 条目:
当应用程序尝试访问用户位置时,系统会显示提示,请求访问:
Android
在 Android 上显示地图并与之交互的配置过程如下:
- 获取 Google Maps API 密钥并将其添加到清单。
- 在清单中指定 Google Play 服务版本号。
- 在清单中指定 Apache HTTP 旧库的要求。
- [可选]在清单中指定 WRITE_EXTERNAL_STORAGE 权限。
- [可选]在清单中指定位置权限。
- [可选] 请求
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
以下屏幕截图中显示了这些内容:
请求运行时位置权限
如果应用程序面向 API 23 或更高版本,并需要访问用户的位置,则必须检查在运行时是否拥有所需的权限,如果没有,则请求获得权限。 这可通过以下操作实现:
在
MainActivity
类中,添加以下字段:const int RequestLocationId = 0; readonly string[] LocationPermissions = { Manifest.Permission.AccessCoarseLocation, Manifest.Permission.AccessFineLocation };
在
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
方法发出权限请求。在
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); } }
此替代处理权限请求的结果。
此代码的总体效果是,当应用程序请求用户的位置时,会显示以下请求权限的对话框:
通用 Windows 平台
在 UWP 上,必须先对应用程序进行身份验证,然后才能显示地图并使用地图服务。 若要对应用程序进行身份验证,必须指定地图身份验证密钥。 有关详细信息,请参阅请求地图身份验证密钥。 然后,应在 FormsMaps.Init("AUTHORIZATION_TOKEN")
方法调用中指定身份验证令牌,以通过必应地图对应用程序进行身份验证。
注意
在 UWP 上,要使用地理编码等地图服务,还必须将 MapService.ServiceToken
属性设置为身份验证密钥值。 这可以通过以下代码行来实现:Windows.Services.Maps.MapService.ServiceToken = "INSERT_AUTH_TOKEN_HERE";
。
此外,如果应用程序需要访问用户的位置,则必须在包清单中启用位置功能。 这可通过以下操作实现:
在解决方案资源管理器中,双击 package.appxmanifest 并选择功能选项卡。
在功能列表中,选中位置框。 这会将
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
控件。