개발일지
react-native-nmap 기능 추가하기 본문
QuadFlask님이 만드신 라이브러리,, 항상 감사하게 사용하고 있습니다ㅠㅠ
프로젝트를 진행하는 도중, 네이버 지도를 사용하게 되었다. 설치부터 우여곡절이 많았는데, 설치 및 구현 관련해서는 나중에 정리해서 포스팅하는걸로,,
각설하고, 내가 필요했던 기능은 다음과 같다. 1) 픽셀 당 미터 수 구하기, 2) 안드로이드에서 서브캡션이 나타나지 않는 문제 해결하기
이렇게 2개를 구현해야 했다.
1. 픽셀 당 미터 수 구하기
네이버 지도의 Android와 iOS 공식 문서를 읽으며 축척에 관한 부분을 읽고, metersPerPixel 메소드가 있는 것을 발견하여 Native코드를 조금 손봐주었다.
// node_modules/react-native-nmap/android/src/main/java/com/github/quadflask/react/navermap/RNNaverMapView.java
package com.github.quadflask.react.navermap;
import android.graphics.PointF;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import com.airbnb.android.react.maps.ViewAttacherGroup;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.LifecycleEventListener;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.WritableNativeMap;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.events.RCTEventEmitter;
import com.naver.maps.geometry.LatLng;
import com.naver.maps.geometry.LatLngBounds;
import com.naver.maps.map.*;
import com.naver.maps.map.util.FusedLocationSource;
import java.util.ArrayList;
import java.util.List;
public class RNNaverMapView extends MapView implements OnMapReadyCallback, NaverMap.OnCameraIdleListener, NaverMap.OnMapClickListener, RNNaverMapViewProps {
private ThemedReactContext themedReactContext;
private FusedLocationSource locationSource;
private NaverMap naverMap;
private ViewAttacherGroup attacherGroup;
private long lastTouch = 0;
private final List<RNNaverMapFeature<?>> features = new ArrayList<>();
...
// 픽셀당 미터 구하기
@Override
public void onCameraIdle() {
Projection projection = naverMap.getProjection();
// 1px 당 미터 구하기
CameraPosition cameraPosition = naverMap.getCameraPosition();
double metersPerPixel = projection.getMetersPerPixel(cameraPosition.target.latitude, cameraPosition.zoom);
WritableMap param = Arguments.createMap();
param.putDouble("latitude", cameraPosition.target.latitude);
param.putDouble("longitude", cameraPosition.target.longitude);
param.putDouble("zoom", cameraPosition.zoom);
// 1px 당 미터 구하기
param.putDouble("metersPerPixel", metersPerPixel);
param.putArray("contentRegion", ReactUtil.toWritableArray(naverMap.getContentRegion()));
param.putArray("coveringRegion", ReactUtil.toWritableArray(naverMap.getCoveringRegion()));
emitEvent("onCameraChange", param);
}
...
}
기존에 구현되어 있는 onCameraIdle 함수에 공식문서에서 확인한 코드들을 넣어 pixel 당 미터수를 반환할 수 있도록 하였다.
iOS에서도 마찬가지로, 공식문서를 참조하여 코드를 추가하였다.
//
// RNNaverMapView.m
//
// Created by flask on 18/04/2019.
// Copyright © 2019 flask. All rights reserved.
//
#import "RNNaverMapView.h"
#import <UIKit/UIKit.h>
#import <React/RCTComponent.h>
#import <React/RCTBridge.h>
#import <React/UIView+React.h>
...
- (void)mapViewIdle:(nonnull NMFMapView *)mapView {
// 픽셀 당 미터 수 구하기
NMFProjection *projection = mapView.projection;
CLLocationDistance metersPerPixel = [projection metersPerPixelAtLatitude:round(mapView.cameraPosition.target.lat) zoom: mapView.cameraPosition.zoom];
if (((RNNaverMapView*)self).onCameraChange != nil)
((RNNaverMapView*)self).onCameraChange(@{
@"latitude" : @(mapView.cameraPosition.target.lat),
@"longitude" : @(mapView.cameraPosition.target.lng),
@"zoom" : @(mapView.cameraPosition.zoom),
@"metersPerPixel": @(metersPerPixel),
@"contentRegion" : pointsToJson(mapView.contentRegion.exteriorRing.points),
@"coveringRegion": pointsToJson(mapView.coveringRegion.exteriorRing.points),
});
}
...
2. 안드로이드 subCaption 설정하기
지도기능을 사용하는 도중 마커의 subCaption을 사용해야 했는데, iOS의 경우에는 subCaption이 잘 작동되었지만, Android의 경우에는 작동이 되지 않았다.
이 또한 네이버 지도 안드로이드 공식문서를 확인하여 코드를 수정하였다.
// RNNaverMapMarker.java
...
public class RNNaverMapMarker extends ClickableRNNaverMapFeature<Marker> implements TrackableView {
private final DraweeHolder<GenericDraweeHierarchy> imageHolder;
private boolean animated = false;
private int duration = 500;
private TimeInterpolator easingFunction;
...
public void setCaption(String text, int textSize, int color, int haloColor, Align... aligns) {
feature.setCaptionText(text);
feature.setCaptionTextSize(textSize);
feature.setCaptionColor(color);
feature.setCaptionHaloColor(haloColor);
feature.setCaptionAligns(aligns);
}
public void removeCaption() {
feature.setCaptionText("");
}
// 서브캡션 생성
public void setSubCaption(String text, int textSize, int color, int haloColor) {
feature.setSubCaptionText(text);
feature.setSubCaptionTextSize(textSize);
feature.setSubCaptionColor(color);
feature.setSubCaptionHaloColor(haloColor);
}
// 서브캡션 지우기
public void removeSubCaption() {
feature.setSubCaptionText("");
}
...
}
setCaption의 양식과, 공식문서의 지원 범위를 확인하여 subCaption을 설정하고 사라질 수 있도록 코드를 추가했다.
또한, react-native에서 넘어오는 설정값들로 subCaption을 설정해야 하기 때문에, RNNaverMapMarkerManager.java 파일도 수정해준다.
...
public class RNNaverMapMarkerManager extends EventEmittableViewGroupManager<RNNaverMapMarker> {
private static final Align DEFAULT_CAPTION_ALIGN = Align.Bottom;
private final DisplayMetrics metrics;
public RNNaverMapMarkerManager(ReactApplicationContext reactContext) {
super(reactContext);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
metrics = new DisplayMetrics();
((WindowManager) reactContext.getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay()
.getRealMetrics(metrics);
} else {
metrics = reactContext.getResources().getDisplayMetrics();
}
}
...
// Javascript에서 넘어오는 subCaption 설정 값 연동
@ReactProp(name = "subCaption")
public void setSubCaption(RNNaverMapMarker view, ReadableMap map) {
if (map == null || !map.hasKey("text")) {
view.removeSubCaption();
return;
}
// 네이버 맵 안드로이드 SDK(https://navermaps.github.io/android-map-sdk/guide-ko/5-2.html) 기준 서브캡션 설정 값을 기준으로 작성
String text = map.getString("text");
int textSize = map.hasKey("textSize") ? map.getInt("textSize") : 16;
int color = map.hasKey("color") ? parseColorString(map.getString("color")) : Color.BLACK;
int haloColor = map.hasKey("haloColor") ? parseColorString(map.getString("haloColor")) : Color.WHITE;
view.setSubCaption(text, textSize, color, haloColor);
}
...
}
'개발일지 > React-Native' 카테고리의 다른 글
RN - Fabric 모드 전환 시 Metro에 연결이 안되는 문제 (0) | 2024.10.30 |
---|---|
React-Navigation과 React-native-safe-area-context의 중복 여백 문제 (1) | 2024.10.17 |
React Navigation - native-stack 뒤로가기 방지 오류 (0) | 2024.09.11 |
나의 늦은 typescript 적응기 (0) | 2022.04.26 |