目录
一、前言
二、iOS原生组件
三、Android原生组件
四、ReactNative整合
一、前言
好好学习,天天向上,是程序员必备的基本品质,作为移动开发者不掌握几门语言还真不行,比如做iOS开发,需要掌握Object-C和Swift,高级一点的还得会C和C++,混合开发盛行之际还得掌握JavaScript,现在Flutter又火了,是不是也得了解一下Dart。学习是无止境的,有时候知识面宽广也是一种优势,但是也不能什么都是蜻蜓点水,起码得精通一门语言,每种语言都会个Hello Word估计找工作都很艰难。接下来一起回顾一下怎么用ReactNative封装Android和iOS原生组件吧!这里主要介绍高德地图如何绘制点标记,刚开始查了一下资料没有找到如何写回调方法,都是仿官方文档写ImageView组件的封装。
二、iOS原生组件
YFRNMapView继承RCTViewManager类就可以被RN调用,下面ReactNative整合会介绍。RCT_EXPORT_VIEW_PROPERTY主要用来传递参数用的,RCTBubblingEventBlock定义的时候一定要用on开头。
@interface YFRNMapView()
@property(nonatomic,strong)YFMapView *mapView ;
@end
@implementation YFRNMapView
RCT_EXPORT_MODULE()
- (UIView *)view {
_mapView=[[YFMapView alloc]initWithFrame:CGRectMake(0, 0, ScreenWidth, ScreenHeight-YFStatusHeight-44)];
return _mapView;
}
RCT_EXPORT_VIEW_PROPERTY(type, NSString)
RCT_EXPORT_VIEW_PROPERTY(deviceArray, NSArray)
RCT_EXPORT_VIEW_PROPERTY(onClick, RCTBubblingEventBlock)
@end
暴露 type, deviceArray,onClick,并重写set方法。
@interface YFMapView ()<MAMapViewDelegate>
@property(nonatomic,strong) MAMapView *mapView;
@end
@implementation YFMapView
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
self.backgroundColor=YFColorRGB(246, 246, 250);
self.mapView = [[MAMapView alloc] initWithFrame:self.bounds];
self.mapView.delegate=self;
self.mapView.rotateEnabled=NO;
[self.mapView setZoomLevel:16];
[self addSubview:self.mapView];
}
return self;
}
- (MAAnnotationView *)mapView:(MAMapView *)mapView viewForAnnotation:(id <MAAnnotation>)annotation
{
if ([annotation isKindOfClass:[MAUserLocation class]]) {
return nil;
}
if ([annotation isKindOfClass:[YFPointAnnotation class]])
{
__weak typeof(self) weakSelf=self;
static NSString *pointReuseIndentifier = @"pointReuseIndentifier";
MAPinAnnotationView*annotationView = (MAPinAnnotationView*)[mapView dequeueReusableAnnotationViewWithIdentifier:pointReuseIndentifier];
if (annotationView == nil)
{
annotationView = [[MAPinAnnotationView alloc] initWithAnnotation:annotation reuseIdentifier:pointReuseIndentifier];
}
annotationView.imageView.contentMode=UIViewContentModeScaleAspectFit;
YFPointAnnotation *yfAnnotation=(YFPointAnnotation*)annotation;
if (yfAnnotation.tag==1) {
[annotationView.imageView sd_setImageWithURL: [NSURL URLWithString:[NSString stringWithFormat:@"%@", yfAnnotation.deviceDict[@"device_icon"]]]];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^(id x) {
NSString *deviceID = [NSString stringWithFormat:@"%@", yfAnnotation.deviceDict[@"device_id"]];
weakSelf.onClick(@{@"deviceId":deviceID});
}];
[annotationView addGestureRecognizer:tap];
return annotationView;
}else{
[annotationView.imageView sd_setImageWithURL: [NSURL URLWithString:[NSString stringWithFormat:@"%@", yfAnnotation.deviceDict[@"status_icon"]]]];
UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] init];
[[tap rac_gestureSignal] subscribeNext:^(id x) {
NSString *dalarmID = [NSString stringWithFormat:@"%@", yfAnnotation.deviceDict[@"alarm_id"]];
weakSelf.onClick(@{@"alarmId":dalarmID});
}];
[annotationView addGestureRecognizer:tap];
return annotationView;
}
return annotationView;
}
return nil;
}
-(void)setOnClick:(RCTBubblingEventBlock)onClick{
_onClick=onClick;
}
-(void)setDeviceArray:(NSArray *)deviceArray{
if (deviceArray) {
_deviceArray=deviceArray;
NSMutableArray *pointArray=[NSMutableArray array];
for (NSDictionary *dict in deviceArray) {
NSLog(@"%@",dict);
YFPointAnnotation *pointAnnotation = [[YFPointAnnotation alloc] init];
float lat=[dict[@"lat"] floatValue];
float lng=[dict[@"lng"] floatValue];
if ([dict[@"gis_type"] intValue]==1) {
if (lat>0 && lng>0) {
pointAnnotation.coordinate =[YFLocationConverter wgs84ToGcj02:CLLocationCoordinate2DMake(lat,lng)];
}else{
continue;
}
}else{
if (lat>0 && lng>0) {
pointAnnotation.coordinate = CLLocationCoordinate2DMake(lat,lng);
}else{
continue;
}
}
pointAnnotation.deviceDict=dict;
if ([self.type isEqualToString:@"alarm"]) {
pointAnnotation.tag=2;
}else{
pointAnnotation.tag=1;
}
[self.mapView addAnnotation:pointAnnotation];
[pointArray addObject:pointAnnotation];
}
[self.mapView showAnnotations:pointArray animated:YES];
}
}
三、Android原生组件
在封装NativeModule (原生模块) 的时候,定义了ReactContextBaseJavaModule的子类,并实现了getName、@ReactMethod注解的供RN调用的通信方法等。创建原生UI组件需要我们定义SimpleViewManager的子类,并实现getName、createViewInstance、@ReactProp注解的方法。
public class YFMapViewManager extends SimpleViewManager<YFRNMapView> {
public static final String REACT_CLASS = "YFRNMapView";
@Override
public String getName() {
return REACT_CLASS;
}
@Override
protected YFRNMapView createViewInstance(ThemedReactContext reactContext) {
return new YFRNMapView(reactContext);
}
@ReactProp(name = "deviceArray")
public void setDeviceArray(YFRNMapView mapView, ReadableArray deviceArray){
mapView.setDeviceArray(deviceArray);
}
@ReactProp(name = "type")
public void setType(YFRNMapView mapView, String type){
mapView.setType(type);
}
@Override
protected void addEventEmitters(
final ThemedReactContext reactContext,
final YFRNMapView view) {
}
@Override
public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
return MapBuilder.<String, Object>builder()
.put("onClick", MapBuilder.of("registrationName", "onClick"))//registrationName 后的名字,RN中方法也要是这个名字否则不执行
.build();
}
}
注册UI模块,定义ReactPackage的子类,即包管理类,并将其添加。还需要将YFMapPackage类添加到Application的getPackages。
public class YFMapPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
List<ViewManager> modules = new ArrayList<>();
modules.add(new YFMapViewManager());
return modules;
}
}
通过receiveEvent实现函数回调,执行onClick方法。
public class YFRNMapView extends FrameLayout {
private MapView MAPVIEW;
private ThemedReactContext CONTEXT;
private ViewGroup.LayoutParams PARAM;
private AMap AMAP;
private String TYPE;
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
public YFRNMapView(ThemedReactContext context) {
super(context);
this.CONTEXT = context;
PARAM = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
}
/**
* Activity onResume后调用view的onAttachedToWindow
*/
@Override
protected void onAttachedToWindow() {
init();
super.onAttachedToWindow();
}
/**
* 初始化控件,定位位置
*/
private void init() {
MAPVIEW = new MapView(CONTEXT);
MAPVIEW.setLayoutParams(PARAM);
this.addView(MAPVIEW);
MAPVIEW.onCreate(CONTEXT.getCurrentActivity().getIntent().getExtras());
setMapOptions();
}
/**
* 设置一些amap的属性
*/
private void setMapOptions() {
AMAP = MAPVIEW.getMap();
AMAP.setMapType(AMap.MAP_TYPE_NORMAL);// 矢量地图模式
AMap.OnMarkerClickListener markerClickListener = new AMap.OnMarkerClickListener() {
// marker 对象被点击时回调的接口
// 返回 true 则表示接口已响应事件,否则返回false
@Override
public boolean onMarkerClick(Marker marker) {
WritableMap eventMap = Arguments.createMap();
if(TYPE == "alarm") {
eventMap.putString("alarmId", marker.getTitle());
}else {
eventMap.putString("deviceId", marker.getTitle());
}
ReactContext reactContext = (ReactContext) getContext();
reactContext.getJSModule(RCTEventEmitter.class).receiveEvent(
getId(),
"onClick",
eventMap);
return true;
}
};
// 绑定 Marker 被点击事件
AMAP.setOnMarkerClickListener(markerClickListener);
}
public void setType(String type){
TYPE=type;
}
public void setDeviceArray(ReadableArray deviceArray) {
LatLngBounds.Builder builder = new LatLngBounds.Builder();
LatLngBounds bounds = null;
for (int i = 0; i < deviceArray.size(); i++) {
ReadableMap map = deviceArray.getMap(i);
double lat = convertToDouble(map.getString("lat"),0);
double lon =convertToDouble(map.getString("lng"),0) ;
LatLng sourceLatLng =new LatLng(lat, lon);
LatLng desLatLng=null;
int gisType=map.getInt("gis_type");
if(lat>0 && lon>0) {
if (gisType == 1) {
CoordinateConverter converter = new CoordinateConverter(getContext());
converter.from(CoordinateConverter.CoordType.GPS);
converter.coord(sourceLatLng);
desLatLng = converter.convert();
} else {
desLatLng = sourceLatLng;
}
Log.i("ReadableMap",desLatLng.toString());
builder.include(desLatLng);
}else {
continue;
}
Log.i("ReadableMap",TYPE);
int ID=0;
String imgURL="";
if(TYPE.equals("alarm")){
ID = map.getInt("alarm_id");
imgURL=map.getString("status_icon");
}else {
ID = map.getInt("device_id");
imgURL=map.getString("device_icon");
}
MarkerOptions markerOptions = new MarkerOptions();
markerOptions.position(desLatLng);
markerOptions.title(String.valueOf(ID));
markerOptions.visible(true);
markerOptions.draggable(false);
Glide.with(getContext()).asBitmap().load(imgURL).diskCacheStrategy(DiskCacheStrategy.ALL).into(new CustomTarget() {
@Override
public void onResourceReady(@NonNull Object resource, @Nullable Transition transition) {
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap((Bitmap)resource);
//添加覆盖物
markerOptions.icon(bitmapDescriptor);
AMAP.addMarker(markerOptions);
}
@Override
public void onLoadCleared(@Nullable Drawable placeholder) { }
});
}
bounds = builder.build();
if (bounds!=null) {
AMAP.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds, 40));
}
}
@Override
protected Parcelable onSaveInstanceState() {
if (CONTEXT.getCurrentActivity().getIntent() != null && CONTEXT.getCurrentActivity().getIntent().getExtras() != null) {
MAPVIEW.onSaveInstanceState(CONTEXT.getCurrentActivity().getIntent().getExtras());
}
return super.onSaveInstanceState();
}
@Override
protected void onDetachedFromWindow() {
this.removeView(MAPVIEW);
MAPVIEW.onDestroy();
super.onDetachedFromWindow();
}
/**
* 对应onResume、对应onPause
*
* @param hasWindowFocus
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
// 对应onResume
MAPVIEW.onResume();
} else {
//对应onPause
MAPVIEW.onPause();
}
}
public static double convertToDouble(String number, double defaultValue) {
if (TextUtils.isEmpty(number)) {
return defaultValue;
}
try {
return Double.parseDouble(number);
} catch (Exception e) {
return defaultValue;
}
}
}
四、ReactNative整合
import React, { Component } from 'react';
import { requireNativeComponent} from 'react-native';
import PropTypes from 'prop-types';
var YFRNMapView = requireNativeComponent('YFRNMapView', MapView);
export default class MapView extends Component {
render() {
return (
<YFRNMapView {...this.props} />
);
}
}
MapView.propTypes = {
type:PropTypes.string,
deviceArray:PropTypes.array,
onClick:PropTypes.func
};
网友评论