Commit d2b294e0 authored by 邱亚涛's avatar 邱亚涛 Committed by Qiu Xiang

高德地图RN版本的很多修复与功能开发

本版本更新列表:
1. 高德地图升级到5.2.1,优化Android工程引用高德地图lib的方式
2. 修复地图组件中Marker或者Overlay无法动态变更调整的问题
3. 修复Android中地图点击、长按、用户位置更新接口未携带定位信息问题
4. 修复Android中地图移除后用户定位未关闭问题
5. 优化地图组件iOS中拖拽返回时卡顿问题
6. 实现地图组件设置地图可展示区域
7. 实现iOS支持animateTo交互操作,实现iOS和Android对duration的支持
8. 实现iOS和Android地图交互大一统接口:animateToMapStatus支持设置多个地图相机参数

9. 修复Android中点击地图后选中的Marker无法自动取消选中问题
10. 修复Marker上zIndex不生效问题
11. 修复iOS中点击Marker后导致地图点击事件回调的问题
12. 修复iOS中自定义的Marker图标在初次渲染时无法展示以及展示位置调整的问题
13. 修复iOS中Marker拖拽后变为大头针问题
14. 修复iOS中Marker自定义InfoWindow位置展示不正确与不接收用户点击事件的问题
15. 修复iOS Marker初始化active参数不生效问题

16. 修复Android中线段opacity不生效问题,实现iOS对opacity的支持
17. 实现iOS支持多颜色线段渐变渲染功能

后续待开发列表:
1. 弄清室内地图不生效问题
2. 支持地图自定义样式设置
3. 更加丰富的地图事件
4. 实现iOS中Overlay支持zIndex设置,添加Overlay地图层次定义

# Conflicts:
#	android/src/main/java/cn/qiuxiang/react/amap3d/AMapView.kt
#	android/src/main/java/cn/qiuxiang/react/amap3d/AMapViewManager.kt
#	components/MapView.js

close #5
parent 41679a8b
package cn.qiuxiang.react.amap3d
import com.amap.api.maps.model.LatLng
import com.amap.api.maps.model.LatLngBounds
import com.facebook.react.bridge.ReadableMap
import android.graphics.Color
/**
* Created by yuekong on 2017/7/4.
*/
class AMapConverter {
companion object {
fun LatLng(map: ReadableMap): LatLng {
return LatLng(map.getDouble("latitude"), map.getDouble("longitude"))
}
fun LatLngDelta(map: ReadableMap): LatLng {
return LatLng(map.getDouble("latitudeDelta"), map.getDouble("longitudeDelta"))
}
fun LatLngBounds(map: ReadableMap): LatLngBounds {
val center = LatLng(map.getMap("center"))
val span = LatLngDelta(map.getMap("span"))
return LatLngBounds(
LatLng(center.latitude - span.latitude, center.longitude - span.longitude),
LatLng(center.latitude + span.latitude, center.longitude + span.longitude)
)
}
fun color(color: Int, opacity: Float): Int {
return Color.argb(((opacity * Color.alpha(color)).toInt()), Color.red(color), Color.green(color), Color.blue(color))
}
}
}
\ No newline at end of file
......@@ -35,6 +35,12 @@ class AMapMarker(context: ThemedReactContext) : ReactViewGroup(context) {
marker?.position = value
}
var zIndex: Float = 0.0f
set(value) {
field = value
marker?.zIndex = value
}
var title = ""
set(value) {
field = value
......@@ -86,7 +92,8 @@ class AMapMarker(context: ThemedReactContext) : ReactViewGroup(context) {
.position(position)
.title(title)
.infoWindowEnable(infoWindowEnabled)
.snippet(snippet))
.snippet(snippet)
.zIndex(zIndex))
if (active) {
marker?.showInfoWindow()
......
......@@ -81,4 +81,9 @@ internal class AMapMarkerManager : ViewGroupManager<AMapMarker>() {
fun setEnabledInfoWindow(marker: AMapMarker, enabled: Boolean) {
marker.infoWindowEnabled = enabled
}
@ReactProp(name = "zIndex")
fun setZInex(marker: AMapMarker, zIndex: Float) {
marker.zIndex = zIndex
}
}
package cn.qiuxiang.react.amap3d
import android.annotation.SuppressLint
import android.view.View
import com.amap.api.maps.AMap
import com.amap.api.maps.CameraUpdateFactory
......@@ -31,10 +32,14 @@ class AMapView(context: ThemedReactContext) : MapView(context) {
map.myLocationStyle = locationStyle
map.setOnMapClickListener { latLng ->
for (marker in markers.values) {
marker.active = false
}
val event = Arguments.createMap()
event.putDouble("latitude", latLng.latitude)
event.putDouble("longitude", latLng.longitude)
emit(id, "onMapClick")
emit(id, "onMapClick", event)
}
map.setOnMapLongClickListener { latLng ->
......@@ -49,7 +54,7 @@ class AMapView(context: ThemedReactContext) : MapView(context) {
event.putDouble("latitude", location.latitude)
event.putDouble("longitude", location.longitude)
event.putDouble("accuracy", location.accuracy.toDouble())
emit(id, "onLocationChange")
emit(id, "onLocationChange", event)
}
map.setOnMarkerClickListener { marker ->
......@@ -86,6 +91,12 @@ class AMapView(context: ThemedReactContext) : MapView(context) {
map.setInfoWindowAdapter(InfoWindowAdapter(context, markers))
}
@SuppressLint("MissingSuperCall")
override fun onDetachedFromWindow() {
super.onDetachedFromWindow()
map.isMyLocationEnabled = false
}
fun addMarker(marker: AMapMarker) {
marker.addToMap(map)
markers.put(marker.marker?.id!!, marker)
......
......@@ -13,9 +13,7 @@ import com.facebook.react.uimanager.annotations.ReactProp
@Suppress("unused")
internal class AMapViewManager : ViewGroupManager<AMapView>() {
companion object {
val ANIMATE_TO_COORDINATE = 1
val ANIMATE_TO_ZOOM_LEVEL = 2
val ANIMATE_TO = 3
val ANIMATE_TO = 1
}
override fun getName(): String {
......@@ -163,6 +161,11 @@ internal class AMapViewManager : ViewGroupManager<AMapView>() {
coordinate.getDouble("longitude"))))
}
@ReactProp(name = "limitRegion")
fun setLimitRegion(view: AMapView, limitRegion: ReadableMap) {
view.map.setMapStatusLimits(AMapConverter.LatLngBounds(limitRegion))
}
@ReactProp(name = "tilt")
fun changeTilt(view: AMapView, tilt: Float) {
view.map.moveCamera(CameraUpdateFactory.changeTilt(tilt))
......
......@@ -7,7 +7,7 @@ import {
findNodeHandle,
requireNativeComponent,
} from 'react-native'
import {LatLng} from './PropTypes'
import {LatLng, Region} from './PropTypes'
import Marker from './Marker'
import Overlay from './Overlay'
import Polyline from './Polyline'
......@@ -100,7 +100,12 @@ class MapView extends Component {
coordinate: LatLng,
/**
* 倾斜角度,取值范围 [0, 60]
* 设置可见地图区域的矩形
*/
limitRegion: Region,
/**
* 设置倾斜角度,取值范围 [0, 60]
*/
tilt: PropTypes.number,
......
......@@ -15,18 +15,11 @@ class Overlay extends Component {
componentDidUpdate() {
setTimeout(() => {
switch (Platform.OS) {
case 'android':
UIManager.dispatchViewManagerCommand(
findNodeHandle(this),
UIManager.AMapOverlay.Commands.update,
null,
)
break;
case 'ios':
NativeModules.AMapOverlayManager.update(findNodeHandle(this))
break;
}
}, 0)
}
......
......@@ -5,4 +5,19 @@ const LatLng = PropTypes.shape({
longitude: PropTypes.number.isRequired,
})
export {LatLng}
const Span = PropTypes.shape({
latitudeDelta: PropTypes.number.isRequired,
longitudeDelta: PropTypes.number.isRequired,
})
const Region = PropTypes.shape({
center: LatLng,
span: Span,
})
export {
LatLng,
Span,
Region,
}
......@@ -6,8 +6,6 @@
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
//地图包、搜索包需要的基础权限
<!--允许程序打开网络套接字-->
<uses-permission android:name="android.permission.INTERNET" />
<!--允许程序设置内置sd卡的写权限-->
......@@ -21,6 +19,8 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!--允许程序访问CellID或WiFi热点来获取粗略的位置-->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<!--允许程序访问GPS定位-->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-sdk
android:minSdkVersion="16"
......
......@@ -3,15 +3,16 @@
@interface AMapMarker : UIView <MAAnnotation, AMapOverlayDelegate>
@property(nonatomic, copy) RCTBubblingEventBlock onPress;
@property(nonatomic, copy) RCTBubblingEventBlock onInfoWindowPress;
@property(nonatomic, copy) RCTBubblingEventBlock onDragStart;
@property(nonatomic, copy) RCTBubblingEventBlock onDrag;
@property(nonatomic, copy) RCTBubblingEventBlock onDragEnd;
@property(nonatomic, copy) RCTBubblingEventBlock onMarkerClick;
@property(nonatomic, copy) RCTBubblingEventBlock onInfoWindowClick;
@property(nonatomic, copy) RCTBubblingEventBlock onMarkerDragStart;
@property(nonatomic, copy) RCTBubblingEventBlock onMarkerDrag;
@property(nonatomic, copy) RCTBubblingEventBlock onMarkerDragEnd;
- (CLLocationCoordinate2D)coordinate;
- (NSString *)title;
- (NSString *)subtitle;
- (void)resetImage;
- (BOOL)active;
- (MAAnnotationView *)annotationView;
......
......@@ -6,15 +6,20 @@
MAPinAnnotationView *_annotationView;
MAPointAnnotation *_annotation;
AMapOverlay *_overlay;
AMapOverlay *_callout;
AMapView *_mapView;
BOOL _active;
UIImage *_image;
CGPoint _centerOffset;
CGPoint _calloutOffset;
}
- (instancetype)init {
self = [super init];
_annotation = [MAPointAnnotation new];
_annotationView = [[MAPinAnnotationView alloc] initWithAnnotation:_annotation reuseIdentifier:nil];
_annotationView.canShowCallout = YES;
self = [super init];
return self;
}
......@@ -55,6 +60,13 @@
}
}
- (void)resetImage {
if (_image) {
_annotationView.image = _image;
_annotationView.centerOffset = _centerOffset;
}
}
- (void)setIcon:(MAPinAnnotationColor)color {
_annotationView.pinColor = color;
}
......@@ -89,22 +101,51 @@
_overlay = (AMapOverlay *) subview;
_overlay.delegate = self;
_annotationView.image = nil;
_image = nil;
}
if (atIndex == 1) {
// TODO: customCalloutView 的位置不太对
_annotationView.customCalloutView = [[MACustomCalloutView alloc] initWithCustomView:(id) subview];
_callout = (AMapOverlay *) subview;
// f**king amap only support button as callout, so below it is.
// more funny thing is callout offset shown right when using button. unbelievable!!!
_callout.delegate = self;
UIButton *button = [[UIButton alloc] initWithFrame:CGRectZero];
[button addSubview:(UIView *) subview];
_annotationView.customCalloutView = [[MACustomCalloutView alloc] initWithCustomView:button];
}
}
}
#pragma mark AMapOverlayDelegate
- (void)update {
- (void)update:(AMapOverlay *)overlay {
if (overlay == _overlay) {
dispatch_async(dispatch_get_main_queue(), ^{
UIGraphicsBeginImageContextWithOptions([_overlay bounds].size, NO, 0.0f);
[_overlay.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
_annotationView.image = image;
_image = image;
});
}
}
- (void)updateLayout:(AMapOverlay *)overlay {
CGFloat width = CGRectGetWidth(overlay.bounds);
CGFloat height = CGRectGetHeight(overlay.bounds);
if (overlay == _overlay) {
// change default image center
_centerOffset = CGPointMake(0, -height / 2);
_annotationView.centerOffset = _centerOffset;
} else if (overlay == _callout) {
// change default callout offset
_annotationView.customCalloutView.bounds = CGRectMake(0, 0, width, height);
}
}
@end
#import <MAMapKit/MAOverlayRenderer.h>
@interface AMapModel : UIView
@interface AMapModel : UIView <MAOverlay>
- (MAOverlayRenderer *)renderer;
@end
#import <React/RCTView.h>
@class AMapOverlay;
@protocol AMapOverlayDelegate <NSObject>
@optional
- (void)update;
- (void)update:(AMapOverlay *)overlay;
- (void)updateLayout:(AMapOverlay *)overlay;
@end
@interface AMapOverlay : RCTView
......
#import "AMapOverlay.h"
#import <React/UIView+React.h>
@implementation AMapOverlay {
}
- (void)update {
[self.delegate update];
[self.delegate update:self];
[self.delegate updateLayout:self];
}
- (void)didUpdateReactSubviews {
[super didUpdateReactSubviews];
[self update];
}
@end
......@@ -9,7 +9,7 @@
@implementation AMapOverlayManager {
}
RCT_EXPORT_MODULE()
RCT_EXPORT_MODULE(AMapOverlay)
- (UIView *)view {
return [AMapOverlay new];
......
......@@ -4,11 +4,23 @@
#pragma ide diagnostic ignored "OCUnusedMethodInspection"
@implementation AMapPolyline {
MAPolyline *_polyline;
MAPolylineRenderer *_renderer;
MAMultiPolyline *_polyline;
MAMultiColoredPolylineRenderer *_renderer;
CGFloat _width;
UIColor *_color;
NSArray *_colors;
CGFloat _opacity;
BOOL _dashed;
BOOL _gradient;
}
- (instancetype)init {
if (self = [super init]) {
_polyline = [MAMultiPolyline polylineWithCoordinates:NULL count:0 drawStyleIndexes:NULL];
}
return self;
}
- (void)setCoordinates:(NSArray<Coordinate *> *)coordinates {
......@@ -16,7 +28,8 @@
for (NSUInteger i = 0; i < coordinates.count; i++) {
coords[i] = coordinates[i].coordinate;
}
_polyline = [MAPolyline polylineWithCoordinates:coords count:coordinates.count];
[_polyline setPolylineWithCoordinates:coords count:coordinates.count];
}
- (void)setWidth:(CGFloat)width {
......@@ -24,16 +37,58 @@
_renderer.lineWidth = width;
}
- (void)setOpacity:(CGFloat)opacity {
opacity = MAX(0.01, opacity); // 线段最低的opacity是0.01不能再小了,否则干掉多好。Android系统上默认为此逻辑。
_opacity = opacity;
_renderer.alpha = opacity;
}
- (void)setColor:(UIColor *)color {
_color = color;
_renderer.strokeColor = color;
}
- (void)setColors:(NSArray *)colors {
// process colors -> strokeColor |egg: [black, black, black, white, white, black] => [black, white, black] + [3, 5]|
NSMutableArray *strokeColors = [[NSMutableArray alloc] init];
NSMutableArray *indexs = [[NSMutableArray alloc] init];
if (colors.count > 0) {
UIColor *lastColor = [colors firstObject];
[strokeColors addObject:lastColor];
for (NSUInteger index = 1; index < colors.count; index++) {
UIColor *color = colors[index];
if (![color isEqual:lastColor]) {
[strokeColors addObject:color];
[indexs addObject:@(index)]; // index is the NEXT color
lastColor = color;
}
}
if (strokeColors.count == 1) {
[indexs addObject:@(colors.count)];
}
}
_colors = strokeColors;
_renderer.strokeColors = strokeColors;
// change polyline
[_polyline setDrawStyleIndexes:@[@(1), @(2)]];
}
- (void)setDashed:(BOOL)dashed {
_dashed = dashed;
_renderer.lineDash = dashed;
}
- (void)setGradient:(BOOL)gradient {
_gradient = gradient;
_renderer.gradient = gradient;
}
- (CLLocationCoordinate2D)coordinate {
return _polyline.coordinate;
}
......@@ -46,10 +101,14 @@
if (_color == nil) {
_color = UIColor.blackColor;
}
_renderer = [[MAPolylineRenderer alloc] initWithPolyline:_polyline];
_renderer = [[MAMultiColoredPolylineRenderer alloc] initWithMultiPolyline:_polyline];
_renderer.lineWidth = _width;
_renderer.strokeColor = _color;
_renderer.strokeColors = _colors;
_renderer.alpha = _opacity;
_renderer.lineDash = _dashed;
_renderer.gradient = _gradient;
return _renderer;
}
......
......@@ -18,7 +18,10 @@ RCT_EXPORT_MODULE()
RCT_EXPORT_VIEW_PROPERTY(coordinates, CoordinateArray)
RCT_EXPORT_VIEW_PROPERTY(width, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(opacity, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(color, UIColor)
RCT_EXPORT_VIEW_PROPERTY(dashed, BOOL)
RCT_EXPORT_VIEW_PROPERTY(gradient, BOOL)
RCT_EXPORT_VIEW_PROPERTY(colors, UIColorArray)
@end
#import <React/UIView+React.h>
#import "AMapView.h"
#import "AMapMarker.h"
#import "AMapPolyline.h"
......@@ -28,6 +29,7 @@
}
- (void)insertReactSubview:(id <RCTComponent>)subview atIndex:(NSInteger)atIndex {
[super insertReactSubview:(UIView *) subview atIndex:atIndex];
if ([subview isKindOfClass:[AMapMarker class]]) {
((AMapMarker *) subview).mapView = self;
[self addAnnotation:(id <MAAnnotation>) subview];
......@@ -38,6 +40,7 @@
}
- (void)removeReactSubview:(id <RCTComponent>)subview {
[super removeReactSubview:(UIView *) subview];
if ([subview isKindOfClass:[AMapMarker class]]) {
[self removeAnnotation:(id <MAAnnotation>) subview];
}
......@@ -46,4 +49,7 @@
}
}
- (void)didUpdateReactSubviews {
}
@end
......@@ -17,6 +17,9 @@ RCT_EXPORT_MODULE()
- (UIView *)view {
AMapView *mapView = [AMapView new];
// when scroll view contains Map maybe we need stop Map Rending Cost.
mapView.runLoopMode = NSDefaultRunLoopMode;
mapView.allowsAnnotationViewSorting = YES;
mapView.centerCoordinate = CLLocationCoordinate2DMake(39.9042, 116.4074);
mapView.zoomLevel = 10;
mapView.delegate = self;
......@@ -39,6 +42,7 @@ RCT_EXPORT_VIEW_PROPERTY(rotateEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(tiltEnabled, BOOL)
RCT_EXPORT_VIEW_PROPERTY(mapType, MAMapType)
RCT_EXPORT_VIEW_PROPERTY(coordinate, CLLocationCoordinate2D)
RCT_EXPORT_VIEW_PROPERTY(limitRegion, MACoordinateRegion)
RCT_EXPORT_VIEW_PROPERTY(tilt, CGFloat)
RCT_EXPORT_VIEW_PROPERTY(onPress, RCTBubblingEventBlock)
......@@ -96,7 +100,11 @@ RCT_EXPORT_METHOD(animateTo:(nonnull NSNumber *)reactTag data:(NSArray *)data) {
if ([annotation isKindOfClass:[AMapMarker class]]) {
AMapMarker *marker = (AMapMarker *) annotation;
if (marker.active) {
dispatch_async(dispatch_get_main_queue(), ^{
// directly call selectAnnotation not work, because of there has no current AnnotationView presentation.
// use RUNLOOP
[mapView selectAnnotation:marker animated:YES];
});
}
return marker.annotationView;
}
......@@ -139,6 +147,9 @@ RCT_EXPORT_METHOD(animateTo:(nonnull NSNumber *)reactTag data:(NSArray *)data) {
@"longitude": @(marker.coordinate.longitude),
});
}
if (newState == MAAnnotationViewDragStateCanceling || newState == MAAnnotationViewDragStateEnding) {
[marker resetImage];
}
}
@end
#import <MAMapKit/MAMapView.h>
#import <MAMapKit/MAGeometry.h>
#import <React/RCTConvert.h>
#import <React/RCTConvert+CoreLocation.h>
#import "Coordinate.h"
......@@ -23,6 +24,29 @@ RCT_ENUM_CONVERTER(MAPinAnnotationColor, (@{
return [[Coordinate alloc] initWithCoordinate: [self CLLocationCoordinate2D:json]];
}
+ (MACoordinateSpan)MACoordinateSpan:(id)json {
json = [self NSDictionary:json];
return (MACoordinateSpan){
[self CLLocationDegrees:json[@"latitudeDelta"]],
[self CLLocationDegrees:json[@"longitudeDelta"]]
};
}
+ (MACoordinateRegion)MACoordinateRegion:(id)json {
return (MACoordinateRegion){
[self CLLocationCoordinate2D:json[@"center"]],
[self MACoordinateSpan:json[@"span"]]
};
}
+ (MAMapStatus *)MAMapStatus:(id)json {
return [MAMapStatus statusWithCenterCoordinate:(json[@"centerCoordinate"] ? [self CLLocationCoordinate2D:json[@"centerCoordinate"]] : (CLLocationCoordinate2D){-1.0f, -1.0f})
zoomLevel:json[@"zoomLevel"] ? [self CGFloat:json[@"zoomLevel"]] : -1
rotationDegree:json[@"rotationDegree"] ? [self CGFloat:json[@"rotationDegree"]] : -1
cameraDegree:json[@"cameraDegree"] ? [self CGFloat:json[@"cameraDegree"]] : -1
screenAnchor:json[@"screenAnchor"] ? [self CGPoint:json[@"screenAnchor"]] : CGPointMake(-1, -1)];
}
RCT_ARRAY_CONVERTER(Coordinate)
@end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment