📌 Intro
카카오 지도 사용방법 두 번째로 현재 위치를 가져와 확인하는 방법에 대해 알아보려고 한다. 바로 결과 화면과 소스코드로 확인해보도록 하자.
📌 결과화면
지도에 있는 파란색 점이 디바이스의 현 위치다. 파란색 점이 나침반 없이 현재 위치를 마커로 표시한 것을 확인할 수 있다. 이와 같은 결과를 만들기 위해서는 아래 코드를 따라가보도록 하자.
📌 소스코드
1. MainActivity.java
package com.example.kakaomap;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.Manifest;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.Signature;
import android.location.LocationManager;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Base64;
import android.util.Log;
import android.view.ViewGroup;
import android.widget.Toast;
import net.daum.mf.map.api.MapPOIItem;
import net.daum.mf.map.api.MapPoint;
import net.daum.mf.map.api.MapView;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class MainActivity extends AppCompatActivity implements MapView.CurrentLocationEventListener, MapView.MapViewEventListener {
private static final String LOG_TAG = "MainActivity";
private MapView mapView;
private ViewGroup mapViewContainer;
private static final int PERMISSIONS_REQUEST_CODE = 100;
String[] REQUIRED_PERMISSIONS = {Manifest.permission.ACCESS_FINE_LOCATION};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 지도 띄우기
mapView = new MapView(this);
mapViewContainer = (ViewGroup) findViewById(R.id.map);
mapViewContainer.addView(mapView);
checkRunTimePermission();
}
// KeyHash 얻는 코드
private void getAppKeyHash(){
PackageInfo packageInfo = null;
try{
packageInfo = getPackageManager().getPackageInfo(getPackageName(), PackageManager.GET_SIGNATURES);
}catch(PackageManager.NameNotFoundException e){
e.printStackTrace();
}
if(packageInfo == null){
Log.e("KeyHash", "KeyHash : null");
}
for(Signature signature : packageInfo.signatures){
try{
MessageDigest md = MessageDigest.getInstance("SHA");
md.update(signature.toByteArray());
Log.e("KeyHash", Base64.encodeToString(md.digest(), Base64.DEFAULT));
}catch(NoSuchAlgorithmException e){
e.printStackTrace();
}
}
}
@Override
public void onCurrentLocationUpdate(MapView mapView, MapPoint currentLocation, float accuracyInMeters) {
MapPoint.GeoCoordinate mapPointGeo = currentLocation.getMapPointGeoCoord();
Log.e(LOG_TAG, String.format("MapView onCurrentLocationUpdate (%f,%f) accuracy (%f)", mapPointGeo.latitude, mapPointGeo.longitude, accuracyInMeters));
}
// ActivityCompat.requestPermissions 를 사용한 퍼미션 요청의 결과를 리턴받는 메소드
@Override
public void onRequestPermissionsResult(int permsRequestCode,
@NonNull String[] permissions,
@NonNull int[] grandResults) {
super.onRequestPermissionsResult(permsRequestCode, permissions, grandResults);
switch (permsRequestCode){
// 요청 코드가 PERMISSIONS_REQUEST_CODE 이면
case PERMISSIONS_REQUEST_CODE:
// 허용을 선택한 경우(permission granted)
if(grandResults.length > 0 && grandResults[0] == PackageManager.PERMISSION_GRANTED){
Log.e("HELL", "Permission Granted");
}
// 거부를 선택한 경우(permission denied)
else{
// "다시 묻지 않음" 을 선택하지 않고 거부한 경우 다시 요청
if(shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)){
ActivityCompat.requestPermissions(
this,
REQUIRED_PERMISSIONS,
PERMISSIONS_REQUEST_CODE);
}
// "다시 묻지 않음" 을 선택하고 거부한 경우 설정창으로 이동
else{
showDialogForPermission();
}
}
}
}
/**
* 런타임 퍼미션 처리
*/
void checkRunTimePermission(){
// 위치 퍼미션을 가지고 있는지 확인
int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION);
// 퍼미션을 가지고 있는 경우
if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED ) {
// ( 안드로이드 6.0 이하 버전은 런타임 퍼미션이 필요없기 때문에 이미 허용된 걸로 인식)
// 위치 값을 가져올 수 있음 -> 현재 위치 세팅
mapView.setCurrentLocationTrackingMode(MapView.CurrentLocationTrackingMode.TrackingModeOnWithoutHeading);
}
// 퍼미션이 없는 경우 요청
else {
Toast.makeText(this,
"이 앱을 실행하려면 위치 접근 권한이 필요합니다.",
Toast.LENGTH_LONG).show();
// onRequestPermissionsResult 함수 콜백
ActivityCompat.requestPermissions(
this,
REQUIRED_PERMISSIONS,
PERMISSIONS_REQUEST_CODE);
}
}
/**
* 다시 묻지 않음을 선택하고 거부한 경우 설정으로 들어갈 수 있는 다이얼로그 생성
*/
private void showDialogForPermission() {
// 다이얼로그 빌더 생성
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("위치 서비스 비활성화");
builder.setMessage("앱을 사용하기 위해서는 위치 서비스가 필요합니다.\n"
+ "위치 설정을 수정하시겠습니까?");
builder.setCancelable(true);
// 다이얼로그에 설정 버튼 추가 및 리스너 바인딩
builder.setPositiveButton("설정", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
// 설정으로 들어가 바로 수정할 수 있도록 인텐트 실행
Intent settingIntent = new Intent(
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
Uri.fromParts("package", getPackageName(), null));
startActivity(settingIntent);
}
});
// 다이얼로그에 취소 버튼 추가 및 리스너 바인딩
builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int id) {
dialog.cancel();
}
});
// 다이얼로그 보여주기
builder.create().show();
}
@Override
public void onCurrentLocationDeviceHeadingUpdate(MapView mapView, float v) {
}
@Override
public void onCurrentLocationUpdateFailed(MapView mapView) {
}
@Override
public void onCurrentLocationUpdateCancelled(MapView mapView) {
}
@Override
public void onMapViewInitialized(MapView mapView) {
}
@Override
public void onMapViewCenterPointMoved(MapView mapView, MapPoint mapPoint) {
}
@Override
public void onMapViewZoomLevelChanged(MapView mapView, int i) {
}
@Override
public void onMapViewSingleTapped(MapView mapView, MapPoint mapPoint) {
}
@Override
public void onMapViewDoubleTapped(MapView mapView, MapPoint mapPoint) {
}
@Override
public void onMapViewLongPressed(MapView mapView, MapPoint mapPoint) {
}
@Override
public void onMapViewDragStarted(MapView mapView, MapPoint mapPoint) {
}
@Override
public void onMapViewDragEnded(MapView mapView, MapPoint mapPoint) {
}
@Override
public void onMapViewMoveFinished(MapView mapView, MapPoint mapPoint) {
}
}
checkRunTimePermission() 함수는 앱을 실행 시킬 때 필요한 퍼미션이 주어졌는지를 확인하고 만약 퍼미션이 없다면 요청하는 함수다. 더 자세한 내용은 추후에 정리하도록 하겠다.
onCurrentLocationDeviceHeadingUpdate() 이후 함수들은 필수로 오버라이드 해야하는 함수이기 때문에 추가해준 것이고 함수 안에 따로 내용을 작성하지 않아도 된다.
setCurrentLocationTrackingMode() 함수는 현재 지도에서 트래킹 모드를 어떤 것으로 설정해줄지 결정하는 역할을 한다. 인자로 TrackingModeOnWithHeading, TrackingModeOnWithoutHeading, TrackingModeOff를 줄 수 있고, 각각에 대한 설명은 다음과 같다.
- TrackingModeOnWithHeading : 나침반 모드를 사용하여 현재 위치를 마커로 표시하는 방법, 현재 위치와 바라보는 방향에 따라 지도가 움직인다.
- TrackingModeOnWithoutHeading : 나침반 없이 현재 위치를 마커로 표시하는 방법, 현재 위치는 표시되지만 움직임에 따라 보고 있는 방향이 나오지 않는다.
- TrackingModeOff : 트래킹 모드를 off하는 방법, 현재 위치와 나침반 모드가 모두 꺼진다.
본인이 원하는 모드에 맞게 인자로 넘겨주면 된다.
onCurrentLocationUpdate() 함수는 이름에서도 알 수 있듯이 현재 위치가 업데이트 되면 콜백되는 함수다. 따라서 현재 위치를 잡으면 setCurrentLocationTrackingMode() 함수에서 정해준 방법으로 지도를 이동시킨다.
2. activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<RelativeLayout
android:id="@+id/map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
</RelativeLayout>
</LinearLayout>
3. AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.kakaomap">
<!-- 인터넷, 위치 권한 -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:usesCleartextTraffic="true"
android:theme="@style/Theme.KakaoMap">
<activity android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data android:name="com.kakao.sdk.AppKey"
android:value="707cd09e072663839a1e58ca07a08dc6"/>
</application>
</manifest>
📌 결과화면
위에서 봤던 결과화면과 동일한 사진이다. 지도에 있는 파란색 점이 디바이스의 현 위치이며, 현재 모드는 TrackingModeOnWithoutHeading로 파란색 점이 나침반 없이 현재 위치를 마커로 표시한 것을 확인할 수 있다.
📌 정리
지금까지 간단하게 카카오 지도에서 현재 위치를 표시하는 방법에 대해 알아보았다. 현재 위치를 받아오는 것 뿐 아니라 다양한 기능이 있기 때문에 차차 정리하려고 한다.
📌 참고
[1] https://blog.naver.com/PostView.nhn?blogId=bgpoilkj&logNo=221972990489