在移动应用开发中,地图是一个很重要的工具,基于地图的定位、导航等特点衍生出了很多著名的移动应用。在 Weex 开发中,如果要使用定位、导航和坐标计算等常见的地图功能,可以使用 weex-amap 插件。
weex-amap 是高德针对 Weex 开发的一款地图插件,在 Eros 开发中,Eros 对 weex-amap 进行二次封装,以便让开发者更集成地图功能。和其他的插件一样,集成此插件需要在原生平台中进行集成。
本文介绍的是如何在 iOS 中集成 weex-amap,以及它的一些核心功能。本文将要介绍的内容如下:
1. 高德地图开发准备工作
1.1 iOS 高德地图开发流程简单介绍
1.2 android 高德地图开发流程简单介绍
1.3 web 高德地图开发流程简单介绍
2. weex-iOS 地图组件扩展方式介绍
2.1 dwd-weex-amap
2.2 dwd-weex-amap-marker
2.3 dwd-weex-amap-info-window
2.4 dwd-weex-amap-circle
2.5 dwd-weex-amap-polygon
2.5 dwd-weex-amap-polyline
3.weex-android 地图组件扩展方式介绍
3.1 dwd-weex-amap
3.2 dwd-weex-amap-marker
3.3 dwd-weex-amap-info-window
3.4 dwd-weex-amap-circle
3.5 dwd-weex-amap-polygon
3.6 dwd-weex-amap-polyline
4.weex-html5 地图组件扩展方式介绍
4.1 dwd-weex-amap
4.2 dwd-weex-amap-marker
4.3 dwd-weex-amap-info-window
4.4 dwd-weex-amap-circle
4.5 dwd-weex-amap-polygon
4.6 dwd-weex-amap-polyline
5. 获取地图数据(例如骑行路径规划)
5.1 weex-iOS 地图骑行路径规划
5.2 weex-android 地图骑行路径规划
5.3 weex-web 地图骑行路径规划
准备工作
1.1 开发流程简绍
1. 使用 CocoaPods 安装 AMapSearch,AMap3DMap SDK 2. 前往高德开放平台控制台申请 iOS Key 3. 配置高德 Key 至 AppDelegate.m 文件
1.2 android 高德地图开发流程简绍
1. 使用 CocoaPods 安装 AMapSearch,AMap3DMap SDK 2. 前往高德开放平台控制台申请 android Key 3.AndroidManifest.xml 的 application 标签中配置 Key 4.AndroidManifest.xml 中配置权限
1.3 HTML5 高德地图开发流程简绍
1. 前往高德开放平台控制台申请 jsAPI Key 2. 可通过 CDN 同步加载方式或使用 require 异步方式来加载 key
参考:高德地图开发文档,vue-amap 开发文档
weex-iOS 地图组件扩展
2.1 weex-amap
地图展示是地图最基本的功能,其常见的效果如下:
如有要在 iOS 中自定义 weex-amap 可以从以下几个步骤完成:
1. 新建 DMapViewComponent 类继承 WXComponent;
2. 在 DMapViewComponent 实现文件中实现 MAMapViewDelegate 代理;
3. 重写 DMapViewComponent 的 loadView 方法加载地图视图并设置自身为代理对象;
4. 在 DMapViewComponent 的初始化函数 viewDidLoad 中做一些准备工作;
5. 在 DMapViewComponent 的 initWithRef 方法中实现属性绑定;
6. 通过 fireEvent 添加适当时机可以触发的事件;
7. 重写 insertSubview 方法来添加子组建包括覆盖物,线,圆等等。
下面是部分的业务实现代码:
@implementation DMapViewComponent
…
// 属性绑定
– (instancetype)initWithRef:(NSString *)ref
type:(NSString*)type
styles:(nullable NSDictionary *)styles
attributes:(nullable NSDictionary *)attributes
events:(nullable NSArray *)events
weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
// 中心点
NSArray *center = [attributes map_safeObjectForKey:@”center”];
_zoomLevel = [[attributes map_safeObjectForKey:@”zoom”] floatValue];
// 是否允许显示指南针
_compass = [[attributes map_safeObjectForKey:@”compass”] boolValue];
// sdkKey
if ([attributes map_safeObjectForKey:@”sdkKey”]) {
[self setAPIKey:[attributes[@”sdkKey”] objectForKey:@”ios”] ? : @””];
}
…
}
return self;
}
// 重写 DMapViewComponent 的 loadView 方法加载地图视图并设置自身为代理对象
– (UIView *) loadView
{
UIWindow *window = [UIApplication sharedApplication].keyWindow;
CGSize windowSize = window.rootViewController.view.frame.size;
self.mapView = [[MAMapView alloc] initWithFrame:CGRectMake(0, 0, windowSize.width, windowSize.height)];
self.mapView.showsUserLocation = _showGeolocation;
self.mapView.delegate = self;
self.mapView.customMapStyleEnabled = YES;
[self.mapView setCustomMapStyleWithWebData:[self getMapData]];
return self.mapView;
}
// 设置地图样式
– (NSData *)getMapData
{
NSString *path = [NSString stringWithFormat:@”%@/gaodeMapStyle.data”, [NSBundle mainBundle].bundlePath];
NSData *data = [NSData dataWithContentsOfFile:path];
return data;
}
– (void)viewDidLoad
{
[super viewDidLoad];
self.mapView.showsScale = _showScale;
self.mapView.showsCompass = _compass;
[self.mapView setCenterCoordinate:_centerCoordinate];
[self.mapView setZoomLevel:_zoomLevel];
}
// 添加覆盖物
– (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index
{
if ([subcomponent isKindOfClass:[DMapRenderer class]]) {
DMapRenderer *overlayRenderer = (DMapRenderer *)subcomponent;
[self addOverlay:overlayRenderer];
}else if ([subcomponent isKindOfClass:[DMapViewMarkerComponent class]]) {
[self addMarker:(DMapViewMarkerComponent *)subcomponent];
}
}
// 更新属性
– (void)updateAttributes:(NSDictionary *)attributes
{
…
if (attributes[@”zoom”]) {
[self setZoomLevel:[attributes[@”zoom”] floatValue]];
}
…
}
#pragma mark – component interface
– (void)setAPIKey:(NSString *)appKey
{
[AMapServices sharedServices].apiKey = appKey;
}
– (void)setZoomLevel:(CGFloat)zoom
{
[self.mapView setZoomLevel:zoom animated:YES];
}
#pragma mark – publish method
– (NSDictionary *)getUserLocation
{
if(self.mapView.userLocation.updating && self.mapView.userLocation.location) {
NSArray *coordinate = @[[NSNumber numberWithDouble:self.mapView.userLocation.location.coordinate.longitude],[NSNumber numberWithDouble:self.mapView.userLocation.location.coordinate.latitude]];
NSDictionary *userDic = @{@”result”:@”success”,@”data”:@{@”position”:coordinate,@”title”:@””}};
return userDic;
}
return @{@”resuldt”:@”false”,@”data”:@””};
}
#pragma mark – mapview delegate
/*!
@brief 根据 anntation 生成对应的 View
*/
– (MAAnnotationView*)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation
{
if ([annotation isKindOfClass:[MAPointAnnotation class]])
{
MAPointAnnotation *pointAnnotation = (MAPointAnnotation *)annotation;
if ([pointAnnotation.component isKindOfClass:[WXMapInfoWindowComponent class]]) {
return [self _generateCustomInfoWindow:mapView viewForAnnotation:pointAnnotation];
}else {
return [self _generateAnnotationView:mapView viewForAnnotation:pointAnnotation];
}
}
return nil;
}
/**
* @brief 当选中一个 annotation views 时,调用此接口
* @param mapView 地图 View
* @param view 选中的 annotation views
*/
– (void)mapView:(MAMapView *)mapView didSelectAnnotationView:(MAAnnotationView *)view
{
MAPointAnnotation *annotation = view.annotation;
for (WXComponent *component in self.subcomponents) {
if ([component isKindOfClass:[WXMapViewMarkerComponent class]] &&
[component.ref isEqualToString:annotation.component.ref]) {
WXMapViewMarkerComponent *marker = (WXMapViewMarkerComponent *)component;
if (marker.clickEvent) {
[marker fireEvent:marker.clickEvent params:[NSDictionary dictionary]];
}
}
}
}
/**
* @brief 当取消选中一个 annotation views 时,调用此接口
* @param mapView 地图 View
* @param view 取消选中的 annotation views
*/
– (void)mapView:(MAMapView *)mapView didDeselectAnnotationView:(MAAnnotationView *)view
{
}
/**
* @brief 地图移动结束后调用此接口
* @param mapView 地图 view
* @param wasUserAction 标识是否是用户动作
*/
– (void)mapView:(MAMapView *)mapView mapDidMoveByUser:(BOOL)wasUserAction
{
if (_isDragend) {
[self fireEvent:@”dragend” params:[NSDictionary dictionary]];
}
}
/** 设置地图缩放级别 */
– (void)setMapViewRegion:(NSMutableArray *)poiArray animated:(BOOL)animated {
NSMutableArray *arrays = [NSMutableArray array];
for (MAPointAnnotation *anot in self.mapView.annotations) {
CLLocationCoordinate2D coordinate = anot.coordinate;
NSDictionary *poidic = [NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:coordinate.latitude * 1000000], @”lat”,
[NSNumber numberWithInt:coordinate.longitude * 1000000], @”lng”, nil];
[arrays addObject:poidic];
}
MACoordinateRegion region = [self getCoordinateMapSpan:arrays];
[self.mapView setRegion:region animated:animated];
}
/** 配置地图 region */
– (MACoordinateRegion)getCoordinateMapSpan:(NSMutableArray *)knightArray {
MACoordinateRegion region;
MACoordinateSpan span;
CLLocationDegrees maxLat = -90;
CLLocationDegrees maxLon = -180;
CLLocationDegrees minLat = 90;
CLLocationDegrees minLon = 180;
if (knightArray && knightArray.count > 1) {
for (int i = 0; i < knightArray.count; i++) {
NSDictionary *knightDictionary = [knightArray objectAtIndex:i];
float lat = [[knightDictionary objectForKey:@”lat”] floatValue] / 1000000;
float lng = [[knightDictionary objectForKey:@”lng”] floatValue] / 1000000;
if(lat > maxLat)
maxLat = lat;
if(lat < minLat)
minLat = lat;
if(lng > maxLon)
maxLon = lng;
if(lng < minLon)
minLon = lng;
}
span.latitudeDelta = (maxLat – minLat) * 2 + 0.005;
span.longitudeDelta = (maxLon – minLon) * 2 + 0.005;
region.center.latitude = (maxLat + minLat) / 2;
region.center.longitude = (maxLon + minLon) / 2;
region.span = span;
} else {
NSDictionary *knightDictionary = [knightArray objectAtIndex:0];
span.latitudeDelta = 0.01;
span.longitudeDelta = 0.01;
float lat = [[knightDictionary objectForKey:@”lat”] floatValue] / 1000000;
float lng = [[knightDictionary objectForKey:@”lng”] floatValue] / 1000000;
if (lat !=0 && lng != 0) {
region.center.longitude = lng;
region.center.latitude = lat;
} else {
region.center = [[ShopLocateManager shared] getLocationCoordinate];
}
region.span = span;
}
return region;
}
…
@end
2.2 weex-amap-marker
marker 主要用于实现锚点,其效果如下:
要在 Weex 中自定义锚点,需要遵循以下几步:
新建 DMapViewMarkerComponent 类继承 WXComponent;
在 DMapViewComponent 中使用 mapview 的 addAnnotation 方法添加 DMapViewMarkerComponent 组件;
在 DMapViewComponent 重写 insertSubview 方法来添加子组建覆盖物。
部分实现代码如下:
– (instancetype)initWithRef:(NSString *)ref
type:(NSString*)type
styles:(nullable NSDictionary *)styles
attributes:(nullable NSDictionary *)attributes
events:(nullable NSArray *)events
weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
if ([events containsObject:@”click”]) {
_clickEvent = @”click”;
}
NSArray *offset = attributes[@”offset”];
if ([WXConvert isValidatedArray:offset]) {
_offset = CGPointMake([WXConvert CGFloat:offset[0]],
[WXConvert CGFloat:offset[1]]);//[WXConvert sizeToWXPixelType:attributes[@”offset”] withInstance:self.weexInstance];
}
if (styles[@”zIndex”]) {
_zIndex = [styles[@”zIndex”] integerValue];
}
_hideCallout = [[attributes map_safeObjectForKey:@”hideCallout”] boolValue];
NSArray *position = [attributes map_safeObjectForKey:@”position”];
if ([WXConvert isValidatedArray:position]) {
_location = [attributes map_safeObjectForKey:@”position”];
}
_title = [attributes map_safeObjectForKey:@”title”];
_icon = [attributes map_safeObjectForKey:@”icon”];
}
return self;
}
– (void)updateAttributes:(NSDictionary *)attributes
{
DMapViewComponent *mapComponent = (DMapViewComponent *)self.supercomponent;
if (attributes[@”title”]) {
_title = attributes[@”title”];
[mapComponent updateTitleMarker:self];
}
if ([attributes map_safeObjectForKey:@”icon”]) {
_icon = attributes[@”icon”];
[mapComponent updateIconMarker:self];
}
NSArray *position = [attributes map_safeObjectForKey:@”position”];
if ([WXConvert isValidatedArray:position]) {
_location = position;
[mapComponent updateLocationMarker:self];
}
}
weex-amap-info-window
weex-amap-info-window 组件主要用于显示地图信息,如地图的图片模式,其效果如下:
要自定义窗体组件,需要用到以下几个步骤:
新建 DMapInfoWindowComponent 类继承 WXComponent;
在 DMapViewComponent 中使用 mapview 的 addAnnotation 方法添加 DMapInfoWindowComponent 组件;
在 DMapViewComponent 重写 insertSubview 方法来添加子组建信息窗体。
部分实现代码如下:
– (instancetype)initWithRef:(NSString *)ref
type:(NSString*)type
styles:(nullable NSDictionary *)styles
attributes:(nullable NSDictionary *)attributes
events:(nullable NSArray *)events
weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
if (attributes[@”open”]) {
_isOpen = [attributes[@”open”] boolValue];
}
}
return self;
}
– (UIView *) loadView
{
return [[DMapInfoWindow alloc] initWithAnnotation:_annotation reuseIdentifier:_identifier];
}
– (void)insertSubview:(WXComponent *)subcomponent atIndex:(NSInteger)index{}
– (void)updateAttributes:(NSDictionary *)attributes
{
[super updateAttributes:attributes];
if (attributes[@”open”])
{
_isOpen = [attributes[@”open”] boolValue];
if (_isOpen) {
[self _addSubView];
}else {
[self _removeViewFromSuperView];
}
}
}
#pragma mark – private method
1. (void)_addSubView
{
[self _removeViewFromSuperView];
[(DMapViewComponent *)self.supercomponent addMarker:self];
}
2. (void)_removeViewFromSuperView
{
[(DMapViewComponent *)self.supercomponent removeMarker:self];
}
2.4 weex-amap-circle
weex-amap-circle 组件主要用于实现画圈功能,如地图范围,其效果如下图所示:
新建 DMapCircleComponent 类继承 WXComponent;
在 DMapViewComponent 中使用 mapview 的 addOverlay 方法添加 DMapCircleComponent 组件;
在 DMapViewComponent 重写 insertSubview 方法来添加子组建圆。
下面是部分实现逻辑:
– (instancetype)initWithRef:(NSString *)ref
type:(NSString*)type
styles:(nullable NSDictionary *)styles
attributes:(nullable NSDictionary *)attributes
events:(nullable NSArray *)events
weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
NSArray *centerArray = [attributes map_safeObjectForKey:@”center”];
if ([WXConvert isValidatedArray:centerArray]) {
_center = centerArray;
}
_radius = [[attributes map_safeObjectForKey:@”radius”] doubleValue];
}
return self;
}
– (void)updateAttributes:(NSDictionary *)attributes
{
NSArray *centerArray = [attributes map_safeObjectForKey:@”center”];
DMapViewComponent *parentComponent = (DMapViewComponent *)self.supercomponent;
if ([WXConvert isValidatedArray:centerArray]) {
_center = centerArray;
[parentComponent removeOverlay:self];
[parentComponent addOverlay:self];
}else if ([[attributes map_safeObjectForKey:@”radius”] doubleValue] >= 0) {
_radius = [[attributes map_safeObjectForKey:@”radius”] doubleValue];
[parentComponent removeOverlay:self];
[parentComponent addOverlay:self];
}else {
[super updateAttributes:attributes];
}
}
2.5 weex-amap-polygon
weex-amap-polygon 主要用于绘制多边形,其效果如下图:
要自定义 weex-amap-polygon,可以从以下步骤着手:
新建 DMapPolygonComponent 类继承 WXComponent;
在 DMapViewComponent 中使用 mapview 的 addOverlay 方法添加 DMapPolygonComponent 组件;
在 DMapViewComponent 重写 insertSubview 方法来添加子组建多边形。
部分实现代码如下:
– (instancetype)initWithRef:(NSString *)ref
type:(NSString*)type
styles:(nullable NSDictionary *)styles
attributes:(nullable NSDictionary *)attributes
events:(nullable NSArray *)events
weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
_fillColor = [attributes map_safeObjectForKey:@”fillColor”];
_fillOpacity = [attributes map_safeObjectForKey:@”fillOpacity”];
}
return self;
}
– (void)updateAttributes:(NSDictionary *)attributes
{
if ([attributes map_safeObjectForKey:@”fillColor”]) {
_fillColor = [attributes map_safeObjectForKey:@”fillColor”];
}else if ([attributes map_safeObjectForKey:@”fillOpacity”]) {
_fillOpacity = [attributes map_safeObjectForKey:@”fillOpacity”];
}else {
[super updateAttributes:attributes];
}
}
2.6 weex-amap-polyline
weex-amap-polyline 组件主要用于在地图上实现划线操作,其最终效果如下图:
在 iOS 中,自定义直接需要从以下几步着手:
新建 DMapPolylineComponent 类继承 WXComponent;
在 DMapViewComponent 中使用 mapview 的 addOverlay 方法添加 DMapPolylineComponent 组件;
在 DMapViewComponent 重写 insertSubview 方法来添加子组建折线。
@implementation DMapPolylineComponent
– (instancetype)initWithRef:(NSString *)ref
type:(NSString*)type
styles:(nullable NSDictionary *)styles
attributes:(nullable NSDictionary *)attributes
events:(nullable NSArray *)events
weexInstance:(WXSDKInstance *)weexInstance
{
self = [super initWithRef:ref type:type styles:styles attributes:attributes events:events weexInstance:weexInstance];
if (self) {
NSArray * pathArray = [attributes map_safeObjectForKey:@”path”];
if ([WXConvert isValidatedArray:pathArray]) {
_path = pathArray;
}
_strokeColor = [attributes map_safeObjectForKey:@”strokeColor”];
_strokeWidth = [[attributes map_safeObjectForKey:@”strokeWidth”] doubleValue];
_strokeOpacity = [[attributes map_safeObjectForKey:@”strokeOpacity”] doubleValue];
_strokeStyle = [attributes map_safeObjectForKey:@”strokeStyle”];
}
_viewLoaded = NO;
return self;
}
– (void)updateAttributes:(NSDictionary *)attributes
{
NSArray * pathArray = [attributes map_safeObjectForKey:@”path”];
DMapViewComponent *parentComponent = (DMapViewComponent *)self.supercomponent;
if (pathArray) {
if ([WXConvert isValidatedArray:pathArray]) {
_path = pathArray;
}
[parentComponent removeOverlay:self];
[parentComponent addOverlay:self];
return;
}else if ([attributes map_safeObjectForKey:@”strokeColor”]) {
_strokeColor = [attributes map_safeObjectForKey:@”strokeColor”];
}else if ([[attributes map_safeObjectForKey:@”strokeWidth”] doubleValue] >= 0) {
_strokeWidth = [[attributes map_safeObjectForKey:@”strokeWidth”] doubleValue];
}else if ([[attributes map_safeObjectForKey:@”strokeOpacity”] doubleValue] >= 0) {
_strokeOpacity = [[attributes map_safeObjectForKey:@”strokeOpacity”] doubleValue];
}else if ([attributes map_safeObjectForKey:@”strokeStyle”]) {
_strokeStyle = [attributes map_safeObjectForKey:@”strokeStyle”];
}
[parentComponent updateOverlayAttributes:self];
}
@end
地图组件扩展
当然,我们也可以不使用 weex-amap,而是直接使用高德地图进行扩展。
3.1 weex-amap
例如,我们自己扩展一个基于原生高德 SDK 生成的 weex-amap 组件。要实现这么一个地图显示的功能,实现的步骤如下:
新建 DMapViewComponent 类继承 WXVContainer 实现 LocationSource;
使用 initComponentHostView(context) 初始化;
在 DMapViewComponent 实现文件中实现初始化 map 对象 initMap,设置 map 的 key;
@WXComponentProp 注解实现属性绑定;
通过 fireEvent 添加适当时机可以触发的事件;
设置 mapview 的 setInfoWindowAdapter,addPolyline,addPolygon,addCircle,addMarker 等方式来实现覆盖物,,线,圆等等。
实现代码可以参考下面的代码:
@Override
protected FrameLayout initComponentHostView(@NonNull Context context) {
mapContainer = new FrameLayout(context) {
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// 解决与 Scroller 的滑动冲突
if (ev.getAction() == MotionEvent.ACTION_UP) {
requestDisallowInterceptTouchEvent(false);
} else {
requestDisallowInterceptTouchEvent(true);
}
return false;
}
};
mapContainer.setBackgroundColor(fakeBackgroundColor);
if (context instanceof Activity) {
mActivity = (Activity) context;
}
return mapContainer;
}
@Override
protected void setHostLayoutParams(FrameLayout host, int width, int height, int left, int right, int top, int bottom) {
super.setHostLayoutParams(host, width, height, left, right, top, bottom);
if (!isMapLoaded.get() && !isInited.get()) {
isInited.set(true);
mapContainer.postDelayed(new Runnable() {
@Override
public void run() {
mMapView = new TextureMapView(getContext());
mapContainer.addView(mMapView, new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
WXLogUtils.e(TAG, “Create MapView ” + mMapView.toString());
initMap();
}
}, 0);
}
}
private void initMap() {
mMapView.onCreate(null);
isMapLoaded.set(false);
if (mAMap == null) {
mAMap = mMapView.getMap();
mAMap.setInfoWindowAdapter(new InfoWindowAdapter(this));
mAMap.setOnMapLoadedListener(new AMap.OnMapLoadedListener() {
@Override
public void onMapLoaded() {
WXLogUtils.e(TAG, “Map loaded”);
isMapLoaded.set(true);
mZoomLevel = mAMap.getCameraPosition().zoom;
mMapView.postDelayed(new Runnable() {
@Override
public void run() {
execPaddingTasks();
}
}, 16);
}
});
// 绑定 Marker 被点击事件
mAMap.setOnMarkerClickListener(new AMap.OnMarkerClickListener() {
// marker 对象被点击时回调的接口
// 返回 true 则表示接口已响应事件,否则返回 false
@Override
public boolean onMarkerClick(Marker marker) {
if (marker != null) {
for (int i = 0; i < getChildCount(); i++) {
if (getChild(i) instanceof DMapMarkerComponent) {
DMapMarkerComponent child = (DMapMarkerComponent) getChild(i);
if (child.getMarker() != null && child.getMarker().getId() == marker.getId()) {
child.onClick();
}
}
}
}
return false;
}
});
mAMap.setOnCameraChangeListener(new AMap.OnCameraChangeListener() {
private boolean mZoomChanged;
@Override
public void onCameraChange(CameraPosition cameraPosition) {
mZoomChanged = mZoomLevel != cameraPosition.zoom;
mZoomLevel = cameraPosition.zoom;
}
@Override
public void onCameraChangeFinish(CameraPosition cameraPosition) {
if (mZoomChanged) {
float scale = mAMap.getScalePerPixel();
float scaleInWeex = scale / WXViewUtils.getWeexPxByReal(scale);
VisibleRegion visibleRegion = mAMap.getProjection().getVisibleRegion();
WXLogUtils.d(TAG, “Visible region: ” + visibleRegion.toString());
Map<String, Object> region = new HashMap<>();
region.put(“northeast”, convertLatLng(visibleRegion.latLngBounds.northeast));
region.put(“southwest”, convertLatLng(visibleRegion.latLngBounds.southwest));
Map<String, Object> data = new HashMap<>();
data.put(“targetCoordinate”, cameraPosition.target.toString());
data.put(“zoom”, cameraPosition.zoom);
data.put(“tilt”, cameraPosition.tilt);
data.put(“bearing”, cameraPosition.bearing);
data.put(“isAbroad”, cameraPosition.isAbroad);
data.put(“scalePerPixel”, scaleInWeex);
data.put(“visibleRegion”, region);
getInstance().fireEvent(getRef(), WeexConstant.EVENT.ZOOM_CHANGE, data);
}
}
});
mAMap.setOnMapTouchListener(new AMap.OnMapTouchListener() {
boolean dragged = false;
@Override
public void onTouch(MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_MOVE:
dragged = true;
break;
case MotionEvent.ACTION_UP:
if (dragged)
getInstance().fireEvent(getRef(), WeexConstant.EVENT.DRAG_CHANGE);
dragged = false;
break;
}
}
});
setUpMap();
}
}
}
3.2 weex-amap-info-window
当然,我们也可以使用它实现 weex-amap-info-window 功能,虽然 weex-amap-info-window 已经被内置到 weex-amap 中。是的的思路如下:
新建 DMapViewMarkerComponent 类继承 WXComponent;
在 DMapViewComponent 中使用 mapview 的 addMarker 方法添加 DMapViewMarkerComponent 组件。在 DMapViewComponent 中使用 mapview 的 addMarker 方法添加 DMapViewMarkerComponent 组件。
private static class InfoWindowAdapter implements AMap.InfoWindowAdapter {
private DMapViewComponent mWXMapViewComponent;
InfoWindowAdapter(DMapViewComponent wxMapViewComponent) {
mWXMapViewComponent = wxMapViewComponent;
}
@Override
public View getInfoWindow(Marker marker) {
return render(marker);
}
@Override
public View getInfoContents(Marker marker) {
return null;
// return render(marker);
}
private View render(Marker marker) {
WXMapInfoWindowComponent wxMapInfoWindowComponent = mWXMapViewComponent.mInfoWindowHashMap.get(marker.getId());
if (wxMapInfoWindowComponent != null) {
WXFrameLayout host = wxMapInfoWindowComponent.getHostView();
// WXFrameLayout content = (WXFrameLayout) host.getChildAt(0);
host.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
host.getLayoutParams().height = ViewGroup.LayoutParams.WRAP_CONTENT;
WXLogUtils.d(TAG, “Info size: ” + host.getMeasuredWidth() + “, ” + host.getMeasuredHeight());
return host;
} else {
WXLogUtils.e(TAG, “WXMapInfoWindowComponent with marker id ” + marker.getId() + ” not found”);
}
return null;
}
}
html5 地图组件扩展
当然,我们可以使用对 html5 的 amap 进行扩展,例如扩展 weex-amap。
4.1 weex-amap
示例代码如下:
<template>
<div class=”amap-page-container”>
<el-amap ref=”map” vid=”amapDemo” :amap-manager=”amapManager” :center=”center” :zoom=”zoom” :plugin=”plugin” :events=”events” class=”amap-demo”>
</el-amap>
<div class=”toolbar”>
<button @click=”getMap()”>get map</button>
</div>
</div>
</template>
<style>
.amap-demo {
height: 300px;
}
</style>
<script>
// NPM 方式
// import {AMapManager} from ‘vue-amap’;
// CDN 方式
let amapManager = new VueAMap.AMapManager();
module.exports = {
data: function() {
return {
amapManager,
zoom: 12,
center: [121.59996, 31.197646],
events: {
init: (o) => {
console.log(o.getCenter())
console.log(this.$refs.map.$$getInstance())
o.getCity(result => {
console.log(result)
})
},
‘moveend’: () => {
},
‘zoomchange’: () => {
},
‘click’: (e) => {
alert(‘map clicked’);
}
},
plugin: [‘ToolBar’, {
pName: ‘MapType’,
defaultType: 0,
events: {
init(o) {
console.log(o);
}
}
}]
};
},
methods: {
getMap() {
// amap vue component
console.log(amapManager._componentMap);
// gaode map instance
console.log(amapManager._map);
}
}
};
</script>
4.2 weex-amap-marker
实现代码如下:
<template>
<div class=”amap-page-container”>
<el-amap vid=”amapDemo” :zoom=”zoom” :center=”center” class=”amap-demo”>
<el-amap-marker vid=”component-marker” :position=”componentMarker.position” :content-render=”componentMarker.contentRender” ></el-amap-marker>
<el-amap-marker v-for=”(marker, index) in markers” :position=”marker.position” :events=”marker.events” :visible=”marker.visible” :draggable=”marker.draggable” :vid=”index”></el-amap-marker>
</el-amap>
<div class=”toolbar”>
<button type=”button” name=”button” v-on:click=”toggleVisible”>toggle first marker</button>
<button type=”button” name=”button” v-on:click=”changePosition”>change position</button>
<button type=”button” name=”button” v-on:click=”chnageDraggle”>change draggle</button>
<button type=”button” name=”button” v-on:click=”addMarker”>add marker</button>
<button type=”button” name=”button” v-on:click=”removeMarker”>remove marker</button>
</div>
</div>
</template>
<style>
.amap-demo {
height: 300px;
}
</style>
<script>
const exampleComponents = {
props: [‘text’],
template: `<div>text from parent: {{text}}</div>`
}
module.exports = {
name: ‘amap-page’,
data() {
return {
count: 1,
slotStyle: {
padding: ‘2px 8px’,
background: ‘#eee’,
color: ‘#333’,
border: ‘1px solid #aaa’
},
zoom: 14,
center: [121.5273285, 31.21515044],
markers: [
{
position: [121.5273285, 31.21515044],
events: {
click: () => {
alert(‘click marker’);
},
dragend: (e) => {
console.log(‘—event—: dragend’)
this.markers[0].position = [e.lnglat.lng, e.lnglat.lat];
}
},
visible: true,
draggable: false,
template: ‘<span>1</span>’,
}
],
renderMarker: {
position: [121.5273285, 31.21715058],
contentRender: (h, instance) => {
// if use jsx you can write in this
// return <div style={{background: ‘#80cbc4’, whiteSpace: ‘nowrap’, border: ‘solid #ddd 1px’, color: ‘#f00’}} onClick={() => …}>marker inner text</div>
return h(
‘div’,
{
style: {background: ‘#80cbc4’, whiteSpace: ‘nowrap’, border: ‘solid #ddd 1px’, color: ‘#f00’},
on: {
click: () => {
const position = this.renderMarker.position;
this.renderMarker.position = [position[0] + 0.002, position[1] – 0.002];
}
}
},
[‘marker inner text’]
)
}
},
componentMarker: {
position: [121.5273285, 31.21315058],
contentRender: (h, instance) => h(exampleComponents,{style: {backgroundColor: ‘#fff’}, props: {text: ‘father is here’}}, [‘xxxxxxx’])
},
slotMarker: {
position: [121.5073285, 31.21715058]
}
};
},
methods: {
onClick() {
this.count += 1;
},
changePosition() {
let position = this.markers[0].position;
this.markers[0].position = [position[0] + 0.002, position[1] – 0.002];
},
chnageDraggle() {
let draggable = this.markers[0].draggable;
this.markers[0].draggable = !draggable;
},
toggleVisible() {
let visableVar = this.markers[0].visible;
this.markers[0].visible = !visableVar;
},
addMarker() {
let marker = {
position: [121.5273285 + (Math.random() – 0.5) * 0.02, 31.21515044 + (Math.random() – 0.5) * 0.02]
};
this.markers.push(marker);
},
removeMarker() {
if (!this.markers.length) return;
this.markers.splice(this.markers.length – 1, 1);
}
}
};
</script>
4.3 weex-amap-info-window
<template>
<div class=”amap-page-container”>
<el-amap vid=”amap” :zoom=”zoom” :center=”center” class=”amap-demo”>
<el-amap-info-window
:position=”currentWindow.position”
:content=”currentWindow.content”
:visible=”currentWindow.visible”
:events=”currentWindow.events”>
</el-amap-info-window>
</el-amap>
<button @click=”switchWindow(0)”>Show First Window</button>
<button @click=”switchWindow(1)”>Show Second Window</button>
</div>
</template>
<style>
.amap-demo {
height: 300px;
}
</style>
<script>
module.exports = {
data () {
return {
zoom: 14,
center: [121.5273285, 31.21515044],
windows: [
{
position: [121.5273285, 31.21515044],
content: ‘Hi! I am here!’,
visible: true,
events: {
close() {
console.log(‘close infowindow1’);
}
}
}, {
position: [121.5375285, 31.21515044],
content: ‘Hi! I am here too!’,
visible: true,
events: {
close() {
console.log(‘close infowindow2’);
}
}
}
],
slotWindow: {
position: [121.5163285, 31.21515044]
},
currentWindow: {
position: [0, 0],
content: ”,
events: {},
visible: false
}
}
},
mounted() {
this.currentWindow = this.windows[0];
},
methods: {
switchWindow(tab) {
this.currentWindow.visible = false;
this.$nextTick(() => {
this.currentWindow = this.windows[tab];
this.currentWindow.visible = true;
});
}
}
};
</script>
API
当然,除了组件之外,我们还可以使用 weex-amap 的 API 来直接操作地图。
5.1 骑行路径 Android 实现
– (void)searchRidingRouteFromLat:(int)fromLat fromLng:(int)fromLng toLat:(int)toLat toLng:(int)toLng {
AMapRidingRouteSearchRequest *request = [[AMapRidingRouteSearchRequest alloc] init];
request.origin = [AMapGeoPoint locationWithLatitude:INT_2_FLOAT(fromLat) / 1000000
longitude:INT_2_FLOAT(fromLng) / 1000000];
request.destination = [AMapGeoPoint locationWithLatitude:INT_2_FLOAT(toLat) / 1000000
longitude:INT_2_FLOAT(toLng) / 1000000];
// 发起路径搜索
[self.aMapSearch AMapRidingRouteSearch:request];
}
– (void)onRouteSearchDone:(AMapRouteSearchBaseRequest *)request response:(AMapRouteSearchResponse *)response {
if(response.route == nil) {
return;
}
// 通过 AMapNavigationSearchResponse 对象处理搜索结果
AMapRoute *route = response.route;
if (route.paths.count > 0) {
AMapPath *amapPath = route.paths[0];
NSArray *coordArray = amapPath.steps;
NSMutableArray *mArray = [NSMutableArray array];
NSArray *start = @[[NSString stringWithFormat:@”%f”, request.origin.longitude], [NSString stringWithFormat:@”%f”, request.origin.latitude]];
[mArray insertObject:start atIndex:0];
for (AMapStep *step in coordArray) {
NSString *polistring = step.polyline;
NSArray *array = [polistring componentsSeparatedByString:@”;”];
for (NSString *str in array) {
NSArray *loc =[str componentsSeparatedByString:@”,”];
[mArray addObject:loc];
}
}
NSArray *end = @[[NSString stringWithFormat:@”%f”, request.destination.longitude], [NSString stringWithFormat:@”%f”, request.destination.latitude]];
[mArray insertObject:end atIndex:mArray.count];
[[DMessageChannelManager shared] postMessage:@”mapLines” andData:@{@”result”: @”success”, @”data”: @{@”mapLines”:mArray}}];
}
}
– (void)AMapSearchRequest:(id)request didFailWithError:(NSError *)error
{
NSLog(@”Error: %@”, error);
}
@end
5.2 骑行路径 iOS 实现
private RouteTask.OnRouteCalculateListener calculateListener = new RouteTask.OnRouteCalculateListener() {
@Override
public void onRouteCalculate(RideRouteResult result, int code) {
HashMap<String, Object> res = new HashMap<>(2);
if (code == 1000 && result != null) {
Map<String, Object> data = new HashMap<>(1);
data.put(“mapLines”, getLatLngList(result.getPaths().get(0), routeTask.getStartPoint(), routeTask.getEndPoint()));
res.put(“result”, “success”);
res.put(“data”, data);
} else {
res.put(“result”, “fail”);
}
String dataJson = new Gson().toJson(res);
NotifyDataManager.getInstance().postMessage(“mapLines”, dataJson);
WXLogUtils.d(“RideRouteResult Json: ” + dataJson);
}
};
routeTask.addRouteCalculateListener(calculateListener);
/**
* 通过首尾经纬度计算走路规划路径中所有经纬度
*
* @param fromLat
* @param fromLng
* @param toLat
* @param toLng
*/
@JSMethod
public void searchRidingRouteFromLat(float fromLat, float fromLng, float toLat, float toLng) {
LocationEntity fromLe = new LocationEntity();
fromLe.lat = fromLat / 1e6;
fromLe.lng = fromLng / 1e6;
LocationEntity toLe = new LocationEntity();
toLe.lat = toLat / 1e6;
toLe.lng = toLng / 1e6;
if (routeTask == null) {
routeTask = RouteTask.getInstance(mWXSDKInstance.getContext());
routeTask.addRouteCalculateListener(calculateListener);
}
routeTask.search(fromLe, toLe);
}
5.3 骑行路径 Web 实现
let stream = weex.requireModule(‘stream’)
stream.fetch({
timeout:20000,
method: ‘GET’,
url: ‘https://restapi.amap.com/v4/direction/bicycling?key=87453539f02a65cd6585210fa2e64dc9&origin=’+fromLng/1000000+’,’+fromLat/1000000+’&destination=’+toLng/1000000+’,’+toLat/1000000,
}, (response) => {
if (response.status == 200) {
let apiData = JSON.parse(response.data)
if(apiData.data){
var polyline= new Array();
polyline[0] = apiData.data.origin.split(“,”);
var polylineList = apiData.data.paths[‘0’].steps[0].polyline.split(“;”);
for(var i=0;i<polylineList.length;i++) {
var polylinePoint = polylineList[i].split(“,”);
polyline.push(polylinePoint);
}
polyline.push(apiData.data.destination.split(“,”)); // 字符分割
callback({“result”:”success”,”data”: {“mapLines”:polyline}});
}
}
}, () => {})