目錄
一、前言
二、iOS原生組件
三、Android原生組件
四、ReactNative整合
一、前言
好好學(xué)習(xí),天天向上,是程序員必備的基本品質(zhì),作為移動開發(fā)者不掌握幾門語言還真不行,比如做iOS開發(fā),需要掌握Object-C和Swift,高級一點(diǎn)的還得會C和C++,混合開發(fā)盛行之際還得掌握J(rèn)avaScript,現(xiàn)在Flutter又火了,是不是也得了解一下Dart。學(xué)習(xí)是無止境的,有時(shí)候知識面寬廣也是一種優(yōu)勢,但是也不能什么都是蜻蜓點(diǎn)水,起碼得精通一門語言,每種語言都會個(gè)Hello Word估計(jì)找工作都很艱難。接下來一起回顧一下怎么用ReactNative封裝Android和iOS原生組件吧!這里主要介紹高德地圖如何繪制點(diǎn)標(biāo)記,剛開始查了一下資料沒有找到如何寫回調(diào)方法,都是仿官方文檔寫ImageView組件的封裝。
二、iOS原生組件
YFRNMapView繼承RCTViewManager類就可以被RN調(diào)用,下面ReactNative整合會介紹。RCT_EXPORT_VIEW_PROPERTY主要用來傳遞參數(shù)用的,RCTBubblingEventBlock定義的時(shí)候一定要用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 (原生模塊) 的時(shí)候,定義了ReactContextBaseJavaModule的子類,并實(shí)現(xiàn)了getName、@ReactMethod注解的供RN調(diào)用的通信方法等。創(chuàng)建原生UI組件需要我們定義SimpleViewManager的子類,并實(shí)現(xiàn)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中方法也要是這個(gè)名字否則不執(zhí)行
.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實(shí)現(xiàn)函數(shù)回調(diào),執(zhí)行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后調(diào)用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();
}
/**
* 設(shè)置一些amap的屬性
*/
private void setMapOptions() {
AMAP = MAPVIEW.getMap();
AMAP.setMapType(AMap.MAP_TYPE_NORMAL);// 矢量地圖模式
AMap.OnMarkerClickListener markerClickListener = new AMap.OnMarkerClickListener() {
// marker 對象被點(diǎn)擊時(shí)回調(diào)的接口
// 返回 true 則表示接口已響應(yīng)事件,否則返回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 被點(diǎn)擊事件
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();
}
/**
* 對應(yīng)onResume、對應(yīng)onPause
*
* @param hasWindowFocus
*/
@Override
public void onWindowFocusChanged(boolean hasWindowFocus) {
super.onWindowFocusChanged(hasWindowFocus);
if (hasWindowFocus) {
// 對應(yīng)onResume
MAPVIEW.onResume();
} else {
//對應(yīng)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
};