Commit baec5fae authored by 7c00's avatar 7c00

重构 Marker,使逻辑更简单清晰

parent 6c27f81b
...@@ -18,6 +18,7 @@ class AMap3DPackage : ReactPackage { ...@@ -18,6 +18,7 @@ class AMap3DPackage : ReactPackage {
return listOf( return listOf(
AMapViewManager(), AMapViewManager(),
AMapMarkerManager(), AMapMarkerManager(),
AMapInfoWindowManager(),
AMapOverlayManager(), AMapOverlayManager(),
AMapPolylineManager(), AMapPolylineManager(),
AMapPolygonManager(), AMapPolygonManager(),
......
package cn.qiuxiang.react.amap3d.maps
import android.content.Context
import com.facebook.react.views.view.ReactViewGroup
class AMapInfoWindow(context: Context) : ReactViewGroup(context) {
init {
addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ ->
val layoutParams = this.layoutParams
if (layoutParams == null || layoutParams.width != this.width || layoutParams.height != this.height) {
this.layoutParams = LayoutParams(this.width, this.height)
}
}
}
}
\ No newline at end of file
package cn.qiuxiang.react.amap3d.maps
import com.facebook.react.uimanager.LayoutShadowNode
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
class AMapInfoWindowManager : ViewGroupManager<AMapInfoWindow>() {
override fun getName(): String {
return "AMapInfoWindow"
}
override fun createViewInstance(reactContext: ThemedReactContext): AMapInfoWindow {
return AMapInfoWindow(reactContext)
}
override fun createShadowNodeInstance(): LayoutShadowNode {
return super.createShadowNodeInstance()
}
}
\ No newline at end of file
...@@ -23,7 +23,7 @@ class AMapMarker(context: Context) : ReactViewGroup(context) { ...@@ -23,7 +23,7 @@ class AMapMarker(context: Context) : ReactViewGroup(context) {
) )
} }
var infoWindow: AMapOverlay? = null var infoWindow: AMapInfoWindow? = null
var infoWindowEnabled: Boolean = true var infoWindowEnabled: Boolean = true
set(value) { set(value) {
...@@ -92,6 +92,12 @@ class AMapMarker(context: Context) : ReactViewGroup(context) { ...@@ -92,6 +92,12 @@ class AMapMarker(context: Context) : ReactViewGroup(context) {
} }
} }
var customIcon: AMapOverlay? = null
set(value) {
field = value
value?.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> updateCustomIcon() }
}
private var bitmapDescriptor: BitmapDescriptor? = null private var bitmapDescriptor: BitmapDescriptor? = null
fun addToMap(map: AMap) { fun addToMap(map: AMap) {
...@@ -115,28 +121,20 @@ class AMapMarker(context: Context) : ReactViewGroup(context) { ...@@ -115,28 +121,20 @@ class AMapMarker(context: Context) : ReactViewGroup(context) {
marker?.isClickable = this.clickable_ marker?.isClickable = this.clickable_
} }
fun setIcon(icon: String) { fun setIconColor(icon: String) {
bitmapDescriptor = COLORS[icon.toUpperCase()]?.let { bitmapDescriptor = COLORS[icon.toUpperCase()]?.let {
BitmapDescriptorFactory.defaultMarker(it) BitmapDescriptorFactory.defaultMarker(it)
} }
marker?.setIcon(bitmapDescriptor) marker?.setIcon(bitmapDescriptor)
} }
fun setIconView(overlay: AMapOverlay) { fun updateCustomIcon() {
overlay.addOnLayoutChangeListener { _, _, _, _, _, _, _, _, _ -> updateIconView(overlay) } customIcon?.let {
overlay.onUpdate { val bitmap = Bitmap.createBitmap(
updateIconView(overlay) it.width, it.height, Bitmap.Config.ARGB_8888)
it.draw(Canvas(bitmap))
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap)
marker?.setIcon(bitmapDescriptor)
} }
} }
/**
* TODO: 如果 IconView 里包含 Image,由于不知道 Image 什么时候加载完毕,会导致 Image 可能无法正确显示
*/
private fun updateIconView(overlay: AMapOverlay) {
val bitmap = Bitmap.createBitmap(
overlay.width, overlay.height, Bitmap.Config.ARGB_8888)
overlay.draw(Canvas(bitmap))
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(bitmap)
marker?.setIcon(bitmapDescriptor)
}
} }
...@@ -2,6 +2,7 @@ package cn.qiuxiang.react.amap3d.maps ...@@ -2,6 +2,7 @@ package cn.qiuxiang.react.amap3d.maps
import android.view.View import android.view.View
import com.amap.api.maps.model.LatLng import com.amap.api.maps.model.LatLng
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.ReadableMap
import com.facebook.react.common.MapBuilder import com.facebook.react.common.MapBuilder
import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.ThemedReactContext
...@@ -19,11 +20,9 @@ internal class AMapMarkerManager : ViewGroupManager<AMapMarker>() { ...@@ -19,11 +20,9 @@ internal class AMapMarkerManager : ViewGroupManager<AMapMarker>() {
} }
override fun addView(marker: AMapMarker, view: View, index: Int) { override fun addView(marker: AMapMarker, view: View, index: Int) {
if (view is AMapOverlay) { when (view) {
when(index) { is AMapOverlay -> marker.customIcon = view
0 -> marker.setIconView(view) is AMapInfoWindow -> marker.infoWindow = view
1 -> marker.infoWindow = view
}
} }
} }
...@@ -37,6 +36,20 @@ internal class AMapMarkerManager : ViewGroupManager<AMapMarker>() { ...@@ -37,6 +36,20 @@ internal class AMapMarkerManager : ViewGroupManager<AMapMarker>() {
) )
} }
companion object {
val UPDATE = 1
}
override fun getCommandsMap(): Map<String, Int> {
return mapOf("update" to UPDATE)
}
override fun receiveCommand(marker: AMapMarker, commandId: Int, args: ReadableArray?) {
when (commandId) {
UPDATE -> marker.updateCustomIcon()
}
}
@ReactProp(name = "title") @ReactProp(name = "title")
fun setTitle(marker: AMapMarker, title: String) { fun setTitle(marker: AMapMarker, title: String) {
marker.title = title marker.title = title
...@@ -81,7 +94,7 @@ internal class AMapMarkerManager : ViewGroupManager<AMapMarker>() { ...@@ -81,7 +94,7 @@ internal class AMapMarkerManager : ViewGroupManager<AMapMarker>() {
@ReactProp(name = "icon") @ReactProp(name = "icon")
fun setIcon(marker: AMapMarker, icon: String) { fun setIcon(marker: AMapMarker, icon: String) {
marker.setIcon(icon) marker.setIconColor(icon)
} }
@ReactProp(name = "infoWindowEnabled") @ReactProp(name = "infoWindowEnabled")
......
...@@ -4,17 +4,4 @@ import android.content.Context ...@@ -4,17 +4,4 @@ import android.content.Context
import com.facebook.react.views.view.ReactViewGroup import com.facebook.react.views.view.ReactViewGroup
class AMapOverlay(context: Context) : ReactViewGroup(context) { class AMapOverlay(context: Context) : ReactViewGroup(context) {
private var updateHandler: (() -> Unit)? = null
fun onUpdate(handler: () -> Unit) {
updateHandler = handler
}
fun update() {
val layoutParams = this.layoutParams
if (layoutParams == null || layoutParams.width != this.width || layoutParams.height != this.height) {
this.layoutParams = LayoutParams(this.width, this.height)
}
updateHandler?.invoke()
}
} }
package cn.qiuxiang.react.amap3d.maps package cn.qiuxiang.react.amap3d.maps
import com.facebook.react.bridge.ReadableArray
import com.facebook.react.uimanager.ThemedReactContext import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager import com.facebook.react.uimanager.ViewGroupManager
...@@ -12,18 +11,4 @@ class AMapOverlayManager : ViewGroupManager<AMapOverlay>() { ...@@ -12,18 +11,4 @@ class AMapOverlayManager : ViewGroupManager<AMapOverlay>() {
override fun createViewInstance(reactContext: ThemedReactContext): AMapOverlay { override fun createViewInstance(reactContext: ThemedReactContext): AMapOverlay {
return AMapOverlay(reactContext) return AMapOverlay(reactContext)
} }
override fun getCommandsMap(): Map<String, Int> {
return mapOf("update" to UPDATE)
}
override fun receiveCommand(overlay: AMapOverlay, commandId: Int, args: ReadableArray?) {
when (commandId) {
UPDATE -> overlay.update()
}
}
companion object {
val UPDATE = 1
}
} }
import {requireNativeComponent, ViewPropTypes} from 'react-native'
export default requireNativeComponent('AMapInfoWindow', {
propTypes: {
...ViewPropTypes,
}
})
import React, {PropTypes, PureComponent} from 'react' import React, {PropTypes} from 'react'
import {Platform, requireNativeComponent, StyleSheet, View, ViewPropTypes} from 'react-native' import {Platform, requireNativeComponent, StyleSheet, View, ViewPropTypes} from 'react-native'
import Overlay from './Overlay' import Overlay from './Overlay'
import InfoWindow from './InfoWindow'
import {LatLng} from '../PropTypes' import {LatLng} from '../PropTypes'
import BaseComponent from '../BaseComponent'
export default class Marker extends PureComponent { export default class Marker extends BaseComponent {
static propTypes = { static propTypes = {
...ViewPropTypes, ...ViewPropTypes,
...@@ -105,36 +107,44 @@ export default class Marker extends PureComponent { ...@@ -105,36 +107,44 @@ export default class Marker extends PureComponent {
/** /**
* 信息窗体点击事件 * 信息窗体点击事件
* 使用自定义 View 会使该事件失效,这时候可以用 Touchable* 代替 *
* Android 在使用自定义 View 时,该事件会失效,这时候可以用 Touchable* 代替
*/ */
onInfoWindowPress: React.PropTypes.func, onInfoWindowPress: React.PropTypes.func,
} }
render() { _renderInfoWindow(view) {
const props = {...this.props} if (view) {
return <InfoWindow style={style.overlay}>{view}</InfoWindow>
let customInfoWindow = <View collapsable={false}/> }
let customMarker = <View collapsable={false}/> }
if (props.children) { componentDidUpdate() {
customInfoWindow = <Overlay style={styles.overlay}>{props.children}</Overlay> if (this._customMarker && Platform.OS === 'android') {
setTimeout(() => this._sendCommand('update'), 0)
} }
}
render() {
const props = {...this.props}
if (typeof props.icon === 'function') { if (typeof props.icon === 'function') {
customMarker = <Overlay style={styles.overlay}>{props.icon()}</Overlay> this._customMarker = <Overlay style={style.overlay}>{props.icon()}</Overlay>
delete props.icon delete props.icon
} }
return <AMapMarker {...props}> return <AMapMarker {...props}>
{customMarker} {this._customMarker}
{customInfoWindow} {this._renderInfoWindow(props.children)}
</AMapMarker> </AMapMarker>
} }
name = 'AMapMarker'
} }
const AMapMarker = requireNativeComponent('AMapMarker', Marker) const AMapMarker = requireNativeComponent('AMapMarker', Marker)
const styles = StyleSheet.create({ const style = StyleSheet.create({
overlay: { overlay: {
position: 'absolute', position: 'absolute',
}, },
......
import React from 'react'
import {requireNativeComponent, ViewPropTypes} from 'react-native' import {requireNativeComponent, ViewPropTypes} from 'react-native'
import BaseComponent from '../BaseComponent'
export default class Overlay extends BaseComponent { /**
static propTypes = { * 用于自定义标记
*/
export default requireNativeComponent('AMapOverlay', {
propTypes: {
...ViewPropTypes, ...ViewPropTypes,
} }
})
_update = () => setTimeout(() => this._sendCommand('update'), 0)
componentDidUpdate = this._update
componentDidMount = this._update
render() {
return <AMapOverlay {...this.props}/>
}
name = 'AMapOverlay'
}
const AMapOverlay = requireNativeComponent('AMapOverlay', Overlay)
import React, {Component} from 'react' import React, {Component} from 'react'
import { import {Alert, Image, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
StyleSheet,
Alert,
Text,
Image,
View,
TouchableOpacity,
} from 'react-native'
import {MapView, Marker} from 'react-native-amap3d' import {MapView, Marker} from 'react-native-amap3d'
export default class MarkerExample extends Component { export default class MarkerExample extends Component {
...@@ -27,69 +20,71 @@ export default class MarkerExample extends Component { ...@@ -27,69 +20,71 @@ export default class MarkerExample extends Component {
}, 1000) }, 1000)
} }
_coordinates = [
{
latitude: 39.806901,
longitude: 116.397972,
},
{
latitude: 39.806901,
longitude: 116.297972,
},
{
latitude: 39.906901,
longitude: 116.397972,
},
{
latitude: 39.706901,
longitude: 116.397972,
},
]
componentWillUnmount() { componentWillUnmount() {
this.mounted = false this.mounted = false
} }
_handleDragEvent = ({nativeEvent}) => _onInfoWindowPress = () => Alert.alert('信息窗口点击事件')
Alert.alert(`新坐标:${nativeEvent.latitude}, ${nativeEvent.longitude}`) _onCustomInfoWindowPress = () => Alert.alert('自定义信息窗口点击事件')
_onDragEvent = ({nativeEvent}) => Alert.alert(`新坐标:${nativeEvent.latitude}, ${nativeEvent.longitude}`)
_handleInfoWindowPress = () => Alert.alert('信息窗口点击事件')
_handleCustomInfoWindowPress = () => Alert.alert('Custom View InfoWindow onPress')
_renderCustomMarker = () =>
<View style={styles.customMarker}>
<Text style={styles.markerText}>{this.state.time.toLocaleTimeString()}</Text>
</View>
_renderImageMarker = () =>
<View style={styles.customIcon}>
<Image style={styles.customIcon} source={require('../../images/flag.png')}/>
</View>
render() { render() {
return <MapView style={StyleSheet.absoluteFill}> return <MapView style={StyleSheet.absoluteFill}>
<Marker <Marker
active active
draggable draggable
title={'一个可拖拽的 Marker ' + this.state.time.toLocaleTimeString()} title={'一个可拖拽的标记 ' + this.state.time.toLocaleTimeString()}
onDragEnd={this._handleDragEvent} onDragEnd={this._onDragEvent}
onInfoWindowPress={this._handleInfoWindowPress} onInfoWindowPress={this._onInfoWindowPress}
coordinate={{ coordinate={this._coordinates[0]}
latitude: 39.806901,
longitude: 116.397972,
}}
/> />
<Marker <Marker
icon='green' icon='green'
onInfoWindowPress={this._handleCustomInfoWindowPress} onInfoWindowPress={this._onCustomInfoWindowPress}
coordinate={{ coordinate={this._coordinates[1]}>
latitude: 39.806901, <TouchableOpacity activeOpacity={0.9} onPress={this._onCustomInfoWindowPress}>
longitude: 116.297972,
}}>
<TouchableOpacity activeOpacity={0.9} onPress={this._handleCustomInfoWindowPress}>
<View style={styles.customInfoWindow}> <View style={styles.customInfoWindow}>
<Text>Custom View InfoWindow</Text> <Text>Custom View InfoWindow</Text>
</View> </View>
</TouchableOpacity> </TouchableOpacity>
</Marker> </Marker>
<Marker <Marker
icon={this._renderImageMarker} icon={() =>
<View style={styles.customIcon}>
<Image style={styles.customIcon} source={require('../../images/flag.png')}/>
</View>
}
title='自定义图片' title='自定义图片'
description="Sometimes you'll have some special properties that you need to expose for the native component, but don't actually want them as part of the API for the associated React component." description="Sometimes you'll have some special properties that you need to expose for the native component, but don't actually want them as part of the API for the associated React component."
coordinate={{ coordinate={this._coordinates[2]}
latitude: 39.906901,
longitude: 116.397972,
}}
/> />
<Marker <Marker
title='Custom View Marker' title='自定义标记'
icon={this._renderCustomMarker} icon={() =>
coordinate={{ <View style={styles.customMarker}>
latitude: 39.706901, <Text style={styles.markerText}>{this.state.time.toLocaleTimeString()}</Text>
longitude: 116.397972, </View>
}} }
coordinate={this._coordinates[3]}
/> />
</MapView> </MapView>
} }
......
#import <React/RCTView.h>
@class AMapInfoWindow;
@protocol AMapInfoWindowDelegate <NSObject>
@optional
- (void)updateInfoWindow:(AMapInfoWindow *)overlay;
@end
@interface AMapInfoWindow : RCTView
@property(nonatomic, strong) id <AMapInfoWindowDelegate> delegate;
@end
#import <React/UIView+React.h>
#import "AMapInfoWindow.h"
@implementation AMapInfoWindow {
}
- (void)didUpdateReactSubviews {
[super didUpdateReactSubviews];
[self.delegate updateInfoWindow:self];
}
@end
#import <React/RCTUIManager.h>
#import "AMapInfoWindow.h"
#pragma ide diagnostic ignored "OCUnusedClassInspection"
@interface AMapInfoWindowManager : RCTViewManager
@end
@implementation AMapInfoWindowManager {
}
RCT_EXPORT_MODULE()
- (UIView *)view {
return [AMapInfoWindow new];
}
@end
#import "AMapView.h" #import "AMapView.h"
#import "AMapOverlay.h" #import "AMapInfoWindow.h"
@interface AMapMarker : MAAnnotationView <MAAnnotation, AMapOverlayDelegate> @interface AMapMarker : MAAnnotationView <MAAnnotation, AMapInfoWindowDelegate>
@property(nonatomic, copy) RCTBubblingEventBlock onPress; @property(nonatomic, copy) RCTBubblingEventBlock onPress;
@property(nonatomic, copy) RCTBubblingEventBlock onInfoWindowPress; @property(nonatomic, copy) RCTBubblingEventBlock onInfoWindowPress;
......
#import <React/UIView+React.h> #import <React/UIView+React.h>
#import "AMapMarker.h" #import "AMapMarker.h"
#import "AMapOverlay.h"
#pragma ide diagnostic ignored "OCUnusedMethodInspection" #pragma ide diagnostic ignored "OCUnusedMethodInspection"
...@@ -8,7 +9,7 @@ ...@@ -8,7 +9,7 @@
MAPinAnnotationView *_pinView; MAPinAnnotationView *_pinView;
MAPinAnnotationColor _pinColor; MAPinAnnotationColor _pinColor;
MACustomCalloutView *_calloutView; MACustomCalloutView *_calloutView;
AMapOverlay *_callout; AMapInfoWindow *_callout;
AMapView *_mapView; AMapView *_mapView;
BOOL _active; BOOL _active;
} }
...@@ -109,12 +110,12 @@ ...@@ -109,12 +110,12 @@
} }
- (void)insertReactSubview:(id <RCTComponent>)subview atIndex:(NSInteger)atIndex { - (void)insertReactSubview:(id <RCTComponent>)subview atIndex:(NSInteger)atIndex {
if (atIndex == 0 && subview.reactSubviews.count > 0) { if ([subview isKindOfClass:[AMapOverlay class]] && subview.reactSubviews.count > 0) {
[super insertReactSubview:subview atIndex:atIndex]; [super insertReactSubview:subview atIndex:atIndex];
} }
if (atIndex == 1 && [subview isKindOfClass:[AMapOverlay class]]) { if ([subview isKindOfClass:[AMapInfoWindow class]]) {
_callout = (AMapOverlay *) subview; _callout = (AMapInfoWindow *) subview;
_callout.delegate = self; _callout.delegate = self;
UIButton *button = [UIButton new]; UIButton *button = [UIButton new];
...@@ -130,9 +131,7 @@ ...@@ -130,9 +131,7 @@
self.bounds = self.reactSubviews[0].bounds; self.bounds = self.reactSubviews[0].bounds;
} }
#pragma mark AMapOverlayDelegate - (void)updateInfoWindow:(AMapInfoWindow *)overlay {
- (void)update:(AMapOverlay *)overlay {
self.customCalloutView.bounds = overlay.bounds; self.customCalloutView.bounds = overlay.bounds;
} }
......
#import <React/RCTView.h> #import <React/RCTView.h>
@class AMapOverlay;
@protocol AMapOverlayDelegate <NSObject>
@optional
- (void)update:(AMapOverlay *)overlay;
@end
@interface AMapOverlay : RCTView @interface AMapOverlay : RCTView
@property(nonatomic, strong) id <AMapOverlayDelegate> delegate;
@end @end
...@@ -4,9 +4,4 @@ ...@@ -4,9 +4,4 @@
@implementation AMapOverlay { @implementation AMapOverlay {
} }
- (void)didUpdateReactSubviews {
[super didUpdateReactSubviews];
[self.delegate update:self];
}
@end @end
...@@ -15,6 +15,4 @@ RCT_EXPORT_MODULE() ...@@ -15,6 +15,4 @@ RCT_EXPORT_MODULE()
return [AMapOverlay new]; return [AMapOverlay new];
} }
RCT_EXPORT_METHOD(update:(nonnull NSNumber *)reactTag) {}
@end @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