点我达weex造轮子-map地图篇

高佳杰

前言:随着公司业务中采用weex技术来实现业务的场景越来越多,对于扎根在即时物流的我们来说地图功能至关重要,然而插件市场现存的地图相关的插件长期没人维护,没有降级功能,路径计算等功能不全,android跑不起等等奇葩的坑让开发者苦不堪言,我们对此进行了探索,出于老业务采用高德地图来实现android,iOS,web相关功能的背景,我们处于兼容的考虑选择了高德地图对weex进行地图相关功能进行扩展,借此机会来记录下的这个虐心的过程,希望能帮助到正在使用weex开发的小伙伴们少走些弯路。

目录
  • 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.高德地图开发准备工作

alt

  • 1.1 iOS高德地图开发流程简单介绍
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高德地图开发流程简单介绍(本文采用elm封装的vue-amap作为试例)
1.前往高德开放平台控制台申请 jsAPI Key  
2.可通过CDN同步加载方式或使用require异步方式来加载key  

注:高德地图开发文档,vue-amap开发文档

2.weex-iOS地图组件扩展方式介绍**
  • 2.1 dwd-weex-amap alt 思路:
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 dwd-weex-amap-marker alt 思路:
1. 新建DMapViewMarkerComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addAnnotation方法添加DMapViewMarkerComponent组件  
3. 在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];

    }
}
  • 2.3 dwd-weex-amap-info-window alt 思路:
1. 新建DMapInfoWindowComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addAnnotation方法添加DMapInfoWindowComponent组件  
3. 在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
- (void)_addSubView
{
    [self _removeViewFromSuperView];
    [(DMapViewComponent *)self.supercomponent addMarker:self];
}

- (void)_removeViewFromSuperView
{
    [(DMapViewComponent *)self.supercomponent removeMarker:self];
}
  • 2.4 dwd-weex-amap-circle alt 思路:
1. 新建DMapCircleComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addOverlay方法添加DMapCircleComponent组件  
3. 在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 dwd-weex-amap-polygon alt 思路:
1. 新建DMapPolygonComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addOverlay方法添加DMapPolygonComponent组件  
3. 在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.5 dwd-weex-amap-polyline alt 思路:
1. 新建DMapPolylineComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addOverlay方法添加DMapPolylineComponent组件  
3. 在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
3.weex-android地图组件扩展方式介绍**
  • 3.1 dwd-weex-amap alt 思路:
1. 新建DMapViewComponent类继承WXVContainer实现LocationSource  
2.使用initComponentHostView(context)初始化  
3.在DMapViewComponent实现文件中实现初始化map对象initMap,设置map的key  
4.@WXComponentProp注解实现属性绑定  
5.通过fireEvent添加适当时机可以触发的事件  
6.设置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 dwd-weex-amap-marker alt 思路:
1. 新建DMapViewMarkerComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addMarker方法添加DMapViewMarkerComponent组件  

部分代码:

 private void initMarker(final String title, final String position, final String icon) {
        postMapOperationTask((WXMapViewComponent) getParent(), new DMapViewComponent.MapOperationTask() {
            @Override
            public void execute(TextureMapView mapView) {
                final MarkerOptions markerOptions = new MarkerOptions();
                //设置Marker可拖动
                markerOptions.draggable(true);
                // 将Marker设置为贴地显示,可以双指下拉地图查看效果
                markerOptions.setFlat(true);
//                markerOptions.icon(BitmapDescriptorFactory.fromView(new View(getContext())));
                Marker marker = mapView.getMap().addMarker(markerOptions);
                setMarkerTitle(marker, title);
                setMarkerPosition(marker, position);
                setMarkerIcon(marker, icon);
                setWidget(marker);
                String ref = (String) getDomObject().getAttrs().get("markerRef");
                ((WXMapViewComponent) getParent()).getCachedMarker().put(ref, DMapMarkerComponent.this);
            }
        });
    }
  • 3.3 dwd-weex-amap-info-window alt 思路:
1. 新建DMapViewMarkerComponent类继承WXComponent  
2.在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;
        }
    }
  • 3.4 dwd-weex-amap-circle alt 思路:
1. 新建DMapViewMarkerComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addCircle方法添加DMapViewMarkerComponent组件  

部分代码:

 private void initCircle() {
        postMapOperationTask((DMapViewComponent) getParent(), new DMapViewComponent.MapOperationTask() {
            @Override
            public void execute(TextureMapView mapView) {
                CircleOptions circleOptions = new CircleOptions();
                circleOptions.strokeColor(mColor);
                circleOptions.strokeWidth(mWeight);
                circleOptions.radius(mRadius);
                circleOptions.fillColor(mFillColor);
                setWidget(mapView.getMap().addCircle(circleOptions));
            }
        });
    }
  • 3.5 dwd-weex-amap-polygon alt 思路:
1. 新建DMapPolygonComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addPolygon方法添加DMapPolygonComponent组件  

部分代码:

   private void initPolygon() {
        postMapOperationTask((DMapViewComponent) getParent(), new DMapViewComponent.MapOperationTask() {
            @Override
            public void execute(TextureMapView mapView) {
                PolygonOptions polygonOptions = new PolygonOptions();
                polygonOptions.addAll(mPosition);
                polygonOptions.strokeColor(mColor);
                polygonOptions.strokeWidth(mWidth);
                setWidget(mapView.getMap().addPolygon(polygonOptions));
            }
        });
    }
  • 3.6 dwd-weex-amap-polyline alt 思路:
1. 新建DMapPolyLineComponent类继承WXComponent  
2.在DMapViewComponent中使用mapview的addPolyline方法添加DMapPolyLineComponent组件  

部分代码:

  private void initPolygon() {
        postMapOperationTask((DMapViewComponent) getParent(), new DMapViewComponent.MapOperationTask() {
            @Override
            public void execute(TextureMapView mapView) {
                PolygonOptions polygonOptions = new PolygonOptions();
                polygonOptions.addAll(mPosition);
                polygonOptions.strokeColor(mColor);
                polygonOptions.strokeWidth(mWidth);
                setWidget(mapView.getMap().addPolygon(polygonOptions));
            }
        });
    }
4.weex-html5地图组件扩展方式介绍**
  • 4.1 dwd-weex-amap alt 基础示例:
<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 dwd-weex-amap-marker alt 基础示例:
<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 dwd-weex-amap-info-window alt 基础示例:
<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>  
  • 4.4 dwd-weex-amap-circle alt 基础示例:
<template>  
    <div class="amap-page-container">
      <el-amap vid="amapDemo" :zoom="zoom" :center="center" class="amap-demo">
        <el-amap-circle v-for="circle in circles" :center="circle.center" :radius="circle.radius" :fill-opacity="circle.fillOpacity" :events="circle.events"></el-amap-circle>
      </el-amap>
    </div>
  </template>

  <style>
    .amap-page-container {
      height: 200px;
    }
  </style>

  <script>
    module.exports = {
      data () {
        return {
          zoom: 15,
          center: [121.5273285, 31.21515044],
          circles: [
            {
              center: [121.5273285, 31.21515044],
              radius: 200,
              fillOpacity: 0.5,
              events: {
                click: () => {
                  alert('click');
                }
              }
            }
          ]
        }
      }
    };
</script>  
  • 4.5 dwd-weex-amap-polygon alt 基础示例:
<template>  
     <div class="amap-page-container">
        <el-amap vid="amapDemo" :zoom="zoom" :center="center" class="amap-demo">
          <el-amap-rectangle v-for="retangle in retangles" :events="retangle.events" :center="retangle.center" :bounds="retangle.bounds" :fill-color="retangle.fillColor" :fill-opacity="retangle.fillOpacity"></el-amap-rectangle>
        </el-amap>
      </div>
  </template>

  <style>
    .amap-page-container {
      height: 200px;
    }
  </style>

  <script>
    module.exports = {
      data () {
        return {
          zoom: 12,
          center: [121.5273285, 31.21515044],
          retangles: [
            {
              center: [121.5273285, 31.21515044],
              bounds: [[121.5273285, 31.21515044], [121.7276285, 31.24545044]],
              fillOpacity: 0.7,
              fillColor: '#ffffff',
              events: {
                click: () => {
                  alert('click');
                }
              }
            }
          ]
        }
      }
    };
</script>  
  • 4.6 dwd-weex-amap-polyline alt 基础示例:
<template>  
    <div class="amap-page-container">
      <el-amap vid="amap" :zoom="zoom" :center="center" class="amap-demo">
        <el-amap-polyline :editable="polyline.editable"  :path="polyline.path" :events="polyline.events"></el-amap-polyline>
      </el-amap>

      <div class="toolbar">
        <button type="button" name="button" v-on:click="changeEditable">change editable</button>
      </div>
    </div>
  </template>

  <style>
    .amap-demo {
      height: 300px;
    }
  </style>

  <script>
    module.exports = {
      data() {
        return {
          zoom: 12,
          center: [121.5273285, 31.25515044],
          polyline: {
            path: [[121.5389385, 31.21515044], [121.5389385, 31.29615044], [121.5273285, 31.21515044]],
            events: {
              click(e) {
                alert('click polyline');
              },
              end: (e) => {
                let newPath = e.target.getPath().map(point => [point.lng, point.lat]);
                console.log(newPath);
              }
            },
            editable: false
          }
        };
      },
      methods: {
        changeEditable() {
          this.polyline.editable = !this.polyline.editable;
        }
      }
    };
</script>  
5.获取地图数据(例如骑行路径规划)**
  • 5.1 weex-iOS地图骑行路径规划 基础示例:
- (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 weex-android地图骑行路径规划 基础示例:
   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 weex-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}});

      }


    }
  }, () => {})