Commit baec5fae authored by 7c00's avatar 7c00

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

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