介绍
本编文章分为2节,第1节是介绍高德地图SDK与Nuget相关的资料,第2节是介绍客户端代码(Xamarin APP)调用SDK时Xamarin.Android与Xamarin.Forms协调交互的方式。
Part 1 高德地图SDK与Nuget相关的资料
高德地图SDK在原生Android方面,提供定制下载与使用gradle从Maven或JCenter集成使用,定制下载的方式浏览这个网址:相关下载-Android 地图SDK | 高德地图API,使用gradle集成的方式浏览这个网址:Android Studio 配置工程-创建工程-开发指南-Android 地图SDK。
定制下载的方式是开发者勾选需要集成的功能后,高德的网站自动将选择的功能合成一个jar包或aar包,而gradle集成的方式像Nuget安装库一样,安装开发者自己选择的库。在高德SDK没有支持gradle集成前只能使用前者,而前者的缺点就是当所有包分开完成绑定工作后,在App端编译会遇到api冲突的异常。而gradle方式则没有这种问题。
不过要注意的是导航SDK已经包含了地图SDK、猎鹰SDK已经包含了定位SDK;所以导航SDK与地图SDK不能同时集成在APP中,只能两者选一;猎鹰SDK与定位SDK不能同时集成在APP中只能两者选一。
Install-Package XamarinLibrary.Xamarin.Android.Amap.Api.Location -Version 4.9.0 Install-Package XamarinLibrary.Xamarin.Android.Amap.Api.Search -Version 7.3.0 Install-Package XamarinLibrary.Xamarin.Android.Amap.Api.Track -Version 1.3.0 Install-Package XamarinLibrary.Xamarin.Android.Amap.Api.3DMap -Version 7.4.0 Install-Package XamarinLibrary.Xamarin.Android.Amap.Api.Navi3DMap -Version 7.4.0
Part 2 调用SDK时Xamarin.Android与Xamarin.Forms协调交互的方式
- 代码项目层级关系
SampleApp这一层其实是.Net Standard类库,而SampleApp.Android这一层则可以看作是main函数所在的一层,通常会把这一层叫做native层。查看native层的Reference会看到它引用了SampleApp这一层,记住这个层级关系有利于分清楚集成第三方SDK与层级调用的关系。
而熟悉.Net Core开发工作的开发者就比较熟悉.Net Standard的作用–跨平台,所以.Net Standard支持的像一系列异步接口、HttpClient、System.Text.Json等这些常用的功能也能直接或通过Nuget安装的方式使用。
但是要注意的是,也因为.Net Standard的跨平台性质,不要将native层的类放到.Net Standard中,例如Android Native的Android.App.Activity类。因为Xamarin.Android能引用.Net Standard,那其他native层如Xamarin.iOS、UWP、WPF也能引用。
所以很多入门Xamarin的开发者的一个问题是Forms的东西怎么在Android或者iOS显示,Android或者iOS的东西怎么在Forms显示。下文讲到的Renderer与MessagingCenter就是其中的两个解决方案。
- 集成SDK
示例代码中包含了全功能的演示,所以引用了导航SDK(含地图SDK)、猎鹰SDK(含位置SDK)与搜索SDK
Xamarin.Forms怎样使用高德地图SDK
根据高德官方文档配置key和配置权限这两节教程,配置native层Properties/AndroidManifest.xml文件即可
- Renderer
界面控件在Android中可以通过xml或者代码创建:
MapView:
mapView = new MapView(this);
AMapNaviView:
<com.amap.api.navi.AMapNaviView android:id="@+id/navi_view" android:layout_width="match_parent" android:layout_height="match_parent"/>
而Xamarin开发需要将这些原生的控件显示到Forms层中,就可以用到Renderer方案,重点是要在override的OnElementChanged函数中将控件设值。
protected override void OnElementChanged(ElementChangedEventArgs<XAMapNaviView> e) { mRelativeLayout = (Context as MainActivity).naviRelativeLayout; (Context as MainActivity).navi.StartNavi(NaviType.Emulator); SetNativeControl(mRelativeLayout); }
以上代码中没有使用基类提供的Inflate函数去获取xml的控件,而是放在MainActivity中使用Inflate获取,是因为SDK中的控件需要进行生命周期管理。一般情况下没有生命周期管理的控件都可以在Renderer中使用Inflate函数获取。
- MessagingCenter
除了原生控件需要在Forms层显示,还需要数据在Forms层与Native层间互相传递,协调代码工作。
这里以高德SDK中查询天气数据为案例进行讲解。
熟悉WPF或者UWP开发工作的.Net开发者比较熟悉以下Forms层中的Xaml代码
<StackLayout> <Label Text="其他数据搜索功能请参照官方文档自行实现"></Label> <Label Text="本示例只作天气查询演示"></Label> <Button Text="Weather Query" x:Name="WeatherBtn" Clicked="WeatherBtn_Clicked"></Button> <Label Text="北京天气"></Label> <Label x:Name="ReportTimeLabel"></Label> <Label x:Name="WeatherLabel"></Label> <Label x:Name="TemperatureLabel"></Label> <Label x:Name="WindDirectionLabel"></Label> <Label x:Name="WindPowerLabel"></Label> <Label x:Name="HumidityLabel"></Label> </StackLayout>
用户点击Button,触发了这个Button的Clicked事件
public const string QueryWeather = "QueryWeather"; private void WeatherBtn_Clicked(object sender, EventArgs e) { MessagingCenter.Send(new object(), QueryWeather); }
而事件函数的函数体内只是使用了MessagingCenter的Send函数
通过查找QueryWeather常量的引用可以看到,消息从SampleApp层Send,在SampleApp.Android层Subscribe
这就是MessagingCenter的用法,MessagingCenter是Pub/Sub设计模式(发布订阅设计模式)的一种实现的类。
订阅了这个消息的native层,在forms层发布消息的时候就会收到触发调用的通知。
继续将业务往下发展
MessagingCenter.Subscribe<object>(this, SearchPage.QueryWeather, sender => { //检索参数为城市和天气类型,实况天气为WEATHER_TYPE_LIVE、天气预报为WEATHER_TYPE_FORECAST var mquery = new WeatherSearchQuery("北京", WeatherSearchQuery.WeatherTypeLive); var mweathersearch = new WeatherSearch(this); mweathersearch.SetOnWeatherSearchListener(new OnWeatherSearchListener()); mweathersearch.Query=mquery; mweathersearch.SearchWeatherAsyn(); //异步搜索 });
当订阅者收到通知后触发查询天气的函数(高德SDK查询天气相关的函数)
查询天气的函数执行成功或者失败后,都会触发OnWeatherSearchListener类内的回调函数OnWeatherLiveSearched
如果天气查询成功了可以使用MessagingCenter将天气数据打包后发送回给forms层,为什么是打包后发送而不是将高德sdk的result变量发送呢,因为高德sdk的result变量不是跨平台的,是不存在于.Net Standard中的,是带有java平台相关的类或者成员的。
所以我们可以像服务器开发工作一般的处理方法一样,使用DTO(data transfer object)传递数据,DTO在这里就只是.Net Standard中的一个自定义类
public class WeatherModel { public string ReportTime { get; set; } public string Weather { get; set; } public string Temperature { get; set; } public string WindDirection { get; set; } public string WindPower { get; set; } public string Humidity { get; set; } }
var weatherlive = weatherLiveResult.LiveResult; var model = new WeatherModel { ReportTime= weatherlive.ReportTime, Humidity= weatherlive.Humidity, Temperature= weatherlive.Temperature, Weather= weatherlive.Weather, WindDirection= weatherlive.WindDirection, WindPower= weatherlive.WindPower }; //发回给forms层 MessagingCenter.Send(new object(), SearchPage.QueryWeatherOk, model);
再通过查找QueryWeatherOK常量的引用,这时会看到接收者就是Sample.App层,而发送者就是Sample.Android层
最后在Forms层将数据设置到Xaml中的Label控件完成整个天气的查询业务
protected override void OnAppearing() { MessagingCenter.Subscribe<object, WeatherModel>(this, QueryWeatherOk, (sender, args) => { Xamarin.Essentials.MainThread.BeginInvokeOnMainThread(() => { ReportTimeLabel.Text = $"{args.ReportTime}发布"; WeatherLabel.Text = args.Weather; TemperatureLabel.Text = $"{args.Temperature}°"; WindDirectionLabel.Text = $"吹{args.WindDirection}风"; WindPowerLabel.Text = $"风力{args.WindPower}级"; HumidityLabel.Text = $"湿度{args.Humidity}"; }); }); }
总结一下整个代码的流程如下图,Forms层发送消息,Native层订阅消息后查询天气,天气查询的回调函数将结果回发到Forms层,Forms层通过UI控件显示数据。
App代码参看
https://github.com/jingliancui/XamarinFormsAMapSDKSample
本文转载自知乎,原文链接:https://zhuanlan.zhihu.com/p/139712723