안드로이드

안드로이드 네이버 지도 사용하기 (Fragment)

하늘을난모기 2017. 12. 26. 22:28

네이버 지도 API를 사용 해보면 알겠지만.. 엄청나게 불편하다.


Fragment에서 사용하는 방법을 위해 엄청난 노가다 끝에 만드는데 성공했다.


1. 라이브러리 추가하기

https://github.com/navermaps/maps.android

가장 최신 버전의 라이브러리를 app단 build.gradle에 추가한다.


(현재 최신)

dependencies {

   compile 'com.naver.maps.open:naver-map-api:2.1.2@aar'

}



2. 필요한 클래스 파일을 만든다.

 - https://github.com/navermaps/maps.android/blob/master/app/src/main/java/com/nhn/android/mapviewer/NMapFragment.java

 - https://github.com/navermaps/maps.android/blob/master/app/src/main/java/com/nhn/android/mapviewer/NMapPOIflagType.java

 - https://github.com/navermaps/maps.android/blob/master/app/src/main/java/com/nhn/android/mapviewer/NMapViewerResourceProvider.java

 - https://github.com/navermaps/maps.android/blob/master/app/src/main/java/com/nhn/android/mapviewer/NMapCalloutCustomOldOverlay.java


이 4개의 클래스 파일을 똑같이 복사하여 넣는다. (패키지를 하나 만들어서 하면 관리가 편하다.)


NMapViewerResourceProvider 클래스만 오류가 날 것이다. (R.drawable 파일이 없어서이다.)


https://github.com/navermaps/maps.android/tree/master/app/src/main/res/drawable-hdpi


이 링크에 있는 아이템을 다운 받아도 된다. 하지만 귀찮으니 일단 R.drawable 부분을 다 주석처리 한다.


NMapViewerResourceProvider 클래스의 351라인을 보면

@Override
public Drawable getCalloutBackground(NMapOverlayItem item) {

if (item instanceof NMapPOIitem) {
NMapPOIitem poiItem = (NMapPOIitem) item;

if (poiItem.showRightButton()) {
Drawable drawable = mContext.getResources().getDrawable(R.drawable.bg_speech);
return drawable;
}
}

Drawable drawable = mContext.getResources().getDrawable(R.drawable.pin_ballon_bg);

return drawable;
}

이런 부분이 있다.

마커를 클릭했을 때 화면에 보여질 말풍선의 백그라운드 배경이다. 원하는 이미지로 넣으면 된다.


우리가 쓸 것은 마커의 시작과 끝이다.

162라인을 보면 이와 같은 부분이 있다.

    // Resource Ids for single icons
private final ResourceIdsOnMap mResourceIdsForMarkerOnMap[] = {
// Spot, Pin icons
// new ResourceIdsOnMap(NMapPOIflagType.PIN, R.drawable.ic_pin_01, R.drawable.ic_pin_02),
// new ResourceIdsOnMap(NMapPOIflagType.SPOT, R.drawable.ic_pin_01, R.drawable.ic_pin_02),

// Direction POI icons: From, To
new ResourceIdsOnMap(NMapPOIflagType.FROM, R.drawable.location_my, R.drawable.location_my),
new ResourceIdsOnMap(NMapPOIflagType.TO, R.drawable.location_t, R.drawable.location_t),
};

FROM은 시작 위치, TO는 마지막 위치다.

원하는 이미지로 바꿔 넣는다.


이후 추가로 아이콘을 더 넣고 싶으면 주석처리 해버린 R.drawable 부분에서 필요한 부분만을 주석을 지우고 그림을 추가하면 된다.

이제 오류를 없앴으니 MapView를 만들자.


public class MapViewFragmentNaver extends NMapFragment 
implements NMapView.OnMapStateChangeListener, NMapPOIdataOverlay.OnStateChangeListener {

이러한 프래그먼트를 만든다.

NMapFragment는 위에 링크에서 NMapFragemnt를 찾아 똑같이 붙여 넣으면 된다.


onMapStateChangeListener()와 OnStateChangeListener()를 상속받고 메서드를 추가한다.


@Override
public void onMapInitHandler(NMapView nMapView, NMapError nMapError) {
if (nMapError == null) {
moveMapCenter();
} else {
Log.e("map init error", nMapError.message);
}
}

상속받은 메서드 중 맵이 초기화 됐을 때의 메서드가 있다.

신기하게도 에러가 null 값 이어야 정상 작동하는 코드다. (가독성이 조금 이상하다.. !=null로 했다가 엄청난 시간을 날려버렸다.....)


이후 onCreateView()함수에 맵 뷰를 초기화 한다.

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_map_view, container, false);
mapView = (NMapView) v.findViewById(R.id.map_view);
mapView.setClientId(getResources().getString(R.string.n_key));
mapView.setClickable(true);
return v;
}

레이아웃의 구성을 잠시 보여주자면..

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">

<com.nhn.android.maps.NMapView
android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent"></com.nhn.android.maps.NMapView>

<LinearLayout
android:layout_width="44dp"
android:layout_height="44dp"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_marginBottom="16dp"
android:layout_marginRight="16dp"
android:background="@color/colorWhite"
android:gravity="center">

<ImageView
android:id="@+id/img_current_location"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/rectangle_6" />
</LinearLayout>


</RelativeLayout>

이렇다. 별거 없다.

다시 Fragment로 넘어가서


이 클래스에서 가장 중요한 부분은

onStart() 메서드다.


mapVIew()가 생성되는 시기가 이상하기에 onStart()에서 잘 다뤄야 한다.


@Override
public void onStart() {
super.onStart();
mapView.setBuiltInZoomControls(true, null);
mapView.setOnMapStateChangeListener(this);
mapController = mapView.getMapController();
mapViewerResourceProvider = new NMapViewerResourceProvider(getActivity());
mapOverlayManager = new NMapOverlayManager(getActivity(), mapView, mapViewerResourceProvider);
moveMapCenter();
}

onStart() 메서드를 오버라이딩하고 mapView와 관련된 것을 넣는다.


맵을 중앙으로 이동하기 위해 moveMapCenter()라는 메서드를 만들어서 사용했다.

위도와 경도는 다양한 방법으로 얻어올 수 있으니 생략.


private void moveMapCenter() {
NGeoPoint currentPoint = new NGeoPoint(lng, lat);
mapController.setMapCenter(currentPoint);

NMapPOIdata poiData = new NMapPOIdata(2, mapViewerResourceProvider);
poiData.addPOIitem(lng, lat, "현재 위치", NMapPOIflagType.FROM, 0);
poiData.addPOIitem(lng2, lat2, "도착 위치", NMapPOIflagType.TO, 0);
poiData.endPOIdata();

NMapPOIdataOverlay poiDataOverlay = mapOverlayManager.createPOIdataOverlay(poiData, null);
poiDataOverlay.showAllPOIdata(0);
poiDataOverlay.setOnStateChangeListener(this);

}

poiData에서 아이템을 추가하며 Flag 값을 지정하여 어떠한 마커를 사용한지 정한다.

앞서 말했듯이 pin이나 spot 등 다른 마커를 사용하고 싶으면

FROM과 TO 대신 NMapPOIflagType에 있는 다른 flag를 사용하면 된다.


poiDataOverlay.showAllPOIdata(0);

이 메서드를 통해 두 마커가 모두 보이도록 자동으로 카메라의 줌을 조정한다.


단순 맵뷰를 띄우고 마커를 두개 찍어서 보여주는 것이 끝났다.

추가로 길찾기를 만들고 싶다면.. 직접 경로를 다 구해서 마커를 열심히 찍으면 될것이다.

(실제로 네이버 가이드에는 길찾기를 위한 경로 그리기가 모든 경로의 꺽이는 부분을 찾아서 직선으로 연결하고 있다..)

추가 사항은 네이버 API 가이드라인을 참고하자.


https://developers.naver.com/docs/map/tutorial/



진짜... 정말 쓰기 힘든 API다.

레퍼런스도 잘 되어있지 않을 뿐더러.. (lng, lat가 v1, v2로 되어있다 -0-)

검색을 해도 제각기 다른 방식으로 구현하고 있다.


지도가 완성 됐으니 끝.

언젠가는 지금 작성한 노가다보다 쉬운 방법을 찾아 내고 말겠다... (스트레스!!!!!!!!!!!!)