Commit baa392b8 authored by Qiu Xiang's avatar Qiu Xiang

使用 shadowNode 处理 infoWindow 的 layout

parent e0734213
...@@ -25,6 +25,7 @@ public class AMap3DPackage implements ReactPackage { ...@@ -25,6 +25,7 @@ public class AMap3DPackage implements ReactPackage {
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) { public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Arrays.<ViewManager>asList( return Arrays.<ViewManager>asList(
new AMapViewManager(), new AMapViewManager(),
new AMapMarkerManager()); new AMapMarkerManager(),
new AMapInfoWindowManager());
} }
} }
package cn.qiuxiang.react.amap3d;
import android.content.Context;
import com.facebook.react.views.view.ReactViewGroup;
public class AMapInfoWindow extends ReactViewGroup {
public AMapInfoWindow(Context context) {
super(context);
}
}
package cn.qiuxiang.react.amap3d;
import android.widget.LinearLayout;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;
import java.util.HashMap;
class AMapInfoWindowManager extends ViewGroupManager<AMapInfoWindow> {
@Override
public String getName() {
return "AMapInfoWindow";
}
@Override
protected AMapInfoWindow createViewInstance(ThemedReactContext reactContext) {
return new AMapInfoWindow(reactContext);
}
@Override
public LayoutShadowNode createShadowNodeInstance() {
// we use a custom shadow node that emits the width/height of the view
// after layout with the updateExtraData method. Without this, we can't generate
// a bitmap of the appropriate width/height of the rendered view.
return new SizeReportingShadowNode();
}
@Override
public void updateExtraData(AMapInfoWindow infoWindow, Object extraData) {
// This method is called from the shadow node with the width/height of the rendered
// marker view.
//
// noinspection unchecked
HashMap<String, Float> data = (HashMap<String, Float>) extraData;
infoWindow.setLayoutParams(new LinearLayout.LayoutParams(
Math.round(data.get("width")), Math.round(data.get("height"))));
}
}
...@@ -25,7 +25,11 @@ class AMapMarkerManager extends ViewGroupManager<AMapMarker> { ...@@ -25,7 +25,11 @@ class AMapMarkerManager extends ViewGroupManager<AMapMarker> {
@Override @Override
public void addView(AMapMarker marker, View view, int index) { public void addView(AMapMarker marker, View view, int index) {
marker.setInfoWindow((ReactViewGroup) view); if (view instanceof AMapInfoWindow) {
marker.setInfoWindow((ReactViewGroup) view);
} else {
super.addView(marker, view, index);
}
} }
@Override @Override
...@@ -78,17 +82,4 @@ class AMapMarkerManager extends ViewGroupManager<AMapMarker> { ...@@ -78,17 +82,4 @@ class AMapMarkerManager extends ViewGroupManager<AMapMarker> {
public void setIcon(AMapMarker marker, String icon) { public void setIcon(AMapMarker marker, String icon) {
marker.setIcon(icon); marker.setIcon(icon);
} }
// 对于 infoWindow,必须手动设置 layoutParams 才能正确显示,
// 但我在 Android 端没有找到监听 infoWindow onLayout 的方法,
// 我的解决办法是在 js 端监听 onLayout,并反馈到这里。
//
// PS.
// react-native-maps 的解决方法是 LayoutShadowNode,详见:
// https://github.com/airbnb/react-native-maps/blob/master/lib/android/src/main/java/com/airbnb/android/react/maps/SizeReportingShadowNode.java
// 这里暂做保留。
@ReactProp(name = "infoWindowLayout")
public void setInfoWindowLayout(AMapMarker marker, ReadableMap layout) {
marker.setInfoWindowLayout(layout.getInt("width"), layout.getInt("height"));
}
} }
/*
Copyright (c) 2015-present, Facebook, Inc.
All rights reserved.
This source code is licensed under the BSD-style license found in the
LICENSE file in the root directory of this source tree. An additional grant
of patent rights can be found in the PATENTS file in the same directory.
*/
package cn.qiuxiang.react.amap3d;
import android.util.Log;
import com.facebook.react.uimanager.LayoutShadowNode;
import com.facebook.react.uimanager.UIViewOperationQueue;
import java.util.HashMap;
import java.util.Map;
// Custom LayoutShadowNode implementation used in conjunction with the AirMapManager
// which sends the width/height of the view after layout occurs.
class SizeReportingShadowNode extends LayoutShadowNode {
@Override
public void onCollectExtraUpdates(UIViewOperationQueue uiViewOperationQueue) {
super.onCollectExtraUpdates(uiViewOperationQueue);
Map<String, Float> data = new HashMap<>();
data.put("width", getLayoutWidth());
data.put("height", getLayoutHeight());
uiViewOperationQueue.enqueueUpdateExtraData(getReactTag(), data);
}
}
import React, {PropTypes, Component} from 'react'
import {requireNativeComponent, View} from 'react-native'
class InfoWindow extends Component {
static propTypes = {
...View.propTypes,
}
render() {
return <AMapInfoWindow {...this.props}/>
}
}
AMapInfoWindow = requireNativeComponent('AMapInfoWindow', InfoWindow)
export default InfoWindow
import React, {PropTypes, Component} from 'react' import React, {PropTypes, Component} from 'react'
import {requireNativeComponent, View} from 'react-native' import {requireNativeComponent, View} from 'react-native'
import Marker from './Marker' import Marker from './Marker'
import InfoWindow from './InfoWindow'
const CoordinatePropType = PropTypes.shape({ const CoordinatePropType = PropTypes.shape({
latitude: PropTypes.number.isRequired, latitude: PropTypes.number.isRequired,
...@@ -142,9 +143,10 @@ class MapView extends Component { ...@@ -142,9 +143,10 @@ class MapView extends Component {
} }
static Marker = Marker static Marker = Marker
static InfoWindow = InfoWindow
} }
AMapView = requireNativeComponent('AMapView', MapView) AMapView = requireNativeComponent('AMapView', MapView)
export default MapView export default MapView
export {MapView, Marker} export {MapView, Marker, InfoWindow}
...@@ -2,6 +2,7 @@ import React, {PropTypes, Component} from 'react' ...@@ -2,6 +2,7 @@ import React, {PropTypes, Component} from 'react'
import {requireNativeComponent, View, PixelRatio} from 'react-native' import {requireNativeComponent, View, PixelRatio} from 'react-native'
import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource' import resolveAssetSource from 'react-native/Libraries/Image/resolveAssetSource'
import {CoordinatePropType} from './PropTypes' import {CoordinatePropType} from './PropTypes'
import InfoWindow from './InfoWindow'
class Marker extends Component { class Marker extends Component {
static propTypes = { static propTypes = {
...@@ -64,8 +65,6 @@ class Marker extends Component { ...@@ -64,8 +65,6 @@ class Marker extends Component {
onInfoWindowPress: React.PropTypes.func, onInfoWindowPress: React.PropTypes.func,
} }
state = {}
_handle(name) { _handle(name) {
return event => { return event => {
if (this.props[name]) { if (this.props[name]) {
...@@ -74,17 +73,9 @@ class Marker extends Component { ...@@ -74,17 +73,9 @@ class Marker extends Component {
} }
} }
_handleLayout(calloutLayout) {
this.setState({infoWindowLayout: {
width: PixelRatio.getPixelSizeForLayoutSize(calloutLayout.width),
height: PixelRatio.getPixelSizeForLayoutSize(calloutLayout.height),
}})
}
render() { render() {
const props = { const props = {
...this.props, ...this.props,
infoWindowLayout: this.state.infoWindowLayout,
onMarkerClick: this._handle('onPress'), onMarkerClick: this._handle('onPress'),
onMarkerDragStart: this._handle('onDragStart'), onMarkerDragStart: this._handle('onDragStart'),
onMarkerDrag: this._handle('onDrag'), onMarkerDrag: this._handle('onDrag'),
...@@ -95,21 +86,25 @@ class Marker extends Component { ...@@ -95,21 +86,25 @@ class Marker extends Component {
props.icon = resolveAssetSource(this.props.icon).uri props.icon = resolveAssetSource(this.props.icon).uri
} }
let customInfoWindow = null
let customMarker = null
if (props.children) { if (props.children) {
props.children = <View customInfoWindow = props.children
{...props.children.props} }
onLayout={event => this._handleLayout(event.nativeEvent.layout)}
collapsable={false}/> if (typeof props.icon === 'function') {
delete props.icon
customMarker = props.icon()
} }
return <AMapMarker {...props}/> return <AMapMarker {...props}>
{customMarker}
{customInfoWindow}
</AMapMarker>
} }
} }
AMapMarker = requireNativeComponent('AMapMarker', Marker, { AMapMarker = requireNativeComponent('AMapMarker', Marker)
nativeOnly: {
infoWindowLayout: true,
},
})
export default Marker export default Marker
import React, {Component} from 'react' import React, {Component} from 'react'
import {StyleSheet, Alert, Text, View} from 'react-native' import {StyleSheet, Alert, Text, View} from 'react-native'
import {MapView, Marker} from 'react-native-amap3d' import {MapView, Marker, InfoWindow} from 'react-native-amap3d'
export default class MarkerComponent extends Component { export default class MarkerComponent extends Component {
static navigationOptions = { static navigationOptions = {
...@@ -29,9 +29,9 @@ export default class MarkerComponent extends Component { ...@@ -29,9 +29,9 @@ export default class MarkerComponent extends Component {
latitude: 39.806901, latitude: 39.806901,
longitude: 116.297972, longitude: 116.297972,
}}> }}>
<View style={styles.customInfoWindow}> <InfoWindow style={styles.customInfoWindow}>
<Text>一个自定义的信息窗口</Text> <Text>一个自定义的信息窗口</Text>
</View> </InfoWindow>
</Marker> </Marker>
<Marker <Marker
icon={require('../images/marker.png')} icon={require('../images/marker.png')}
...@@ -41,6 +41,13 @@ export default class MarkerComponent extends Component { ...@@ -41,6 +41,13 @@ export default class MarkerComponent extends Component {
longitude: 116.397972, longitude: 116.397972,
}} }}
/> />
<Marker
title='自定义 View 图标'
coordinate={{
latitude: 39.706901,
longitude: 116.397972,
}}
/>
</MapView> </MapView>
} }
} }
......
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