ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Google Map에 현재 위치 표시하기( FusedLocationProviderClient 사용)
    Android/Google Map 2019. 8. 15. 10:41




    FusedLocationProviderClient를 사용하여 Google Map에 현재 위치를 표시하는 예제입니다.

    FusedLocationProviderClient는 Deprecated된 FusedLocationApi를 대체하게 되며 Google Play services 11.6.0 이상에서 사용하는 것을 권장하고 있습니다. 


    2018. 10.2

    2019. 7. 19  androidx 관련 내용 추가 

    2019. 8. 15 SupportMapFragment로 변경




    테스트 환경은 다음과 같습니다.


    Android Studio 3.4.2

    Android 9.+  ( API 29 )





    1. 구현된 내용


    2. 실행 과정 설명


    3. 프로그램 흐름


    4. 소스 코드


    5. 관련 포스팅

        5.1. Google Maps Android API 사용 방법 및 예제

        5.2. Places API Web Service를 사용하여 Android Google Map에 현재 위치 주변의 음식점 표시하기

        5.3. GenyMotion 가상머신에 Google Apps설치하여 Google Maps Android API 테스트 하기




    1. 구현된 내용


    현재 구현된 내용은 다음과 같습니다.


    • 현재 위치를 지도 상에 마커로 표시해줍니다.

    • 디바이스의 위치 서비스(GPS)가 비활성화 되어 있는 경우 사용자가 활성화하도록 요구합니다.

    • 디바이스의 운영체제 버전이 안드로이드 6.0이상일 경우에는 위치 관련 퍼미션을 런타임에 요구합니다.

    • 현재 위치가 변경되면 계속 카메라가 이동하여 현재 위치를 중심으로 지도를 보여주게 됩니다. 




    2. 실행 과정 설명


    처음 실행하면 지도의 초기위치를 서울로 이동시키고 위치 정보 사용을 위한 퍼미션 허가를 사용자에게 요청합니다.  

    Android 6.0 미만 운영체제를 사용하는 안드로이드 디바이스에서는 보이지 않습니다. 




    처음 실행하면 안드로이드 디바이스가 현재 위치를 찾는데 시간이 걸립니다. 

    적절한 조치를 안해주면 아프리카 대륙 옆에 있는 바다가 보입니다.



    여러가지 방법이 있지만 저는 서울로 초기위치를 이동시켜주는 방법을 사용했습니다. 

    onMapReady() 메소드에서 서울로 카메라를 이동시켜줍니다.

    그래서 앱에서 GPS가 사용 못하더라도 서울을 보여주게 됩니다. 


    다른 방법은 마지막에 안드로이드폰이 위치를 인식했던 정보를 가져오는 방법이 있습니다.

    아래 링크를 참고하세요..

    https://developer.android.com/training/location/retrieve-current.html 




    위치 서비스가 비활성화되어 있는 경우 활성화 여부를 사용자에게 물어봅니다.





    설정을 선택했다면, 위치 정보 설정이 보입니다. 

    활성화시키고 백버튼을 눌러 앱으로 돌아오면  





    현재 위치에 코드에서 추가한 파란색 마커와 구글맵에서 현재 위치를 표시하는데 사용되는 파란색 동그라미가 표시됩니다.





    마커를 터치하면 현재 위치의 주소와 좌표가 표시됩니다.




    안드로이드 디바이스를 움직이면 현재 위치가 달라짐에 따라 파란색 마커의 위치가 이동하게 됩니다.



    3. 프로그램 흐름


    앱을 실행시키면 다음 순서대로 메소드들이 실행됩니다.

    (런타임 퍼미션, GPS 활성화를 제외한 상태입니다.)


    2018-10-02 11:35:40.478 15364-15364/? D/googlemap_example: onCreate

    2018-10-02 11:35:40.481 15364-15364/? D/googlemap_example: onStart

    2018-10-02 11:35:40.483 15364-15364/? D/googlemap_example: onStart : call mFusedLocationClient.requestLocationUpdates

    2018-10-02 11:35:40.493 15364-15364/? D/googlemap_example: onMapReady :

    2018-10-02 11:35:40.574 15364-15364/? D/googlemap_example: startLocationUpdates : call mFusedLocationClient.requestLocationUpdates




    이제 일정시간마다 현재 위치를 업데이트합니다.


    2018-10-02 11:35:40.644 15364-15364/? D/googlemap_example: onLocationResult : 위도:37.5173504 경도:126.8945132

    2018-10-02 11:35:43.604 15364-15364/? D/googlemap_example: onLocationResult : 위도:37.5174425 경도:126.8949426

    2018-10-02 11:35:44.230 15364-15364/? D/googlemap_example: onLocationResult : 위도:37.5174422 경도:126.8949434

    2018-10-02 11:35:45.244 15364-15364/? D/googlemap_example: onLocationResult : 위도:37.5174414 경도:126.8949452

    2018-10-02 11:35:46.258 15364-15364/? D/googlemap_example: onLocationResult : 위도:37.5174395 경도:126.8949457




    백버튼을 눌러서 앱종료시 다음 메소드가 실행됩니다.


    2018-10-02 11:36:59.110 15364-15364/? D/googlemap_example: onStop : call stopLocationUpdates




    4. 소스 코드


    다음 포스팅을 기반으로 수정합니다.  중복되는 내용은 빠져있습니다.



    Google Maps Android API 사용 방법 및 예제

    http://webnautes.tistory.com/647 

      





    1. 매네페스트 파일 AndroidManifest.xml<manifest> 태그 하위요소로 <uses-permission> 태그를 사용하여 위치정보 접근을 위한 퍼미션을 추가해줍니다.



    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.tistory.webnautes.googlemapsandroidapiexample">

       <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"




    2. buidle.gradle를 수정합니다. Snackbar를 사용하기 위해 com.google.android.material:material를 추가해야 합니다. 


    dependencies {
        implementation fileTree(dir: 'libs', include: ['*.jar'])
        implementation 'androidx.appcompat:appcompat:1.0.2'
        implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
        implementation 'com.google.android.gms:play-services-maps:17.0.0'
        implementation 'com.google.android.gms:play-services-location:17.0.0'
        implementation 'com.google.android.material:material:1.1.0-alpha09'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'androidx.test:runner:1.2.0'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    }


    추가한 후, Sync Now를 클릭합니다.




    3.  Snackbar를 사용하기 위해 레이아웃에 ID를 추가해야 합니다. 


    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/layout_main"
        tools:context=".MainActivity" >

        <fragment
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:id="@+id/map"
            tools:context=".MapsActivity"
            android:name="com.google.android.gms.maps.SupportMapFragment" />


    </LinearLayout>



        

    4.  MainActivity.java를 다음 내용으로 변경합니다.




    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.PackageManager;
    import android.location.Address;
    import android.location.Geocoder;
    import android.location.Location;
    import android.location.LocationManager;
    import android.os.Bundle;
    import android.os.Looper;
    import android.util.Log;
    import android.view.View;
    import android.view.WindowManager;
    import android.widget.Toast;

    import com.google.android.gms.location.FusedLocationProviderClient;
    import com.google.android.gms.location.LocationCallback;
    import com.google.android.gms.location.LocationRequest;
    import com.google.android.gms.location.LocationResult;
    import com.google.android.gms.location.LocationServices;
    import com.google.android.gms.location.LocationSettingsRequest;
    import com.google.android.gms.maps.CameraUpdate;
    import com.google.android.gms.maps.CameraUpdateFactory;
    import com.google.android.gms.maps.GoogleMap;
    import com.google.android.gms.maps.OnMapReadyCallback;
    import com.google.android.gms.maps.SupportMapFragment;
    import com.google.android.gms.maps.model.BitmapDescriptorFactory;
    import com.google.android.gms.maps.model.LatLng;
    import com.google.android.gms.maps.model.Marker;
    import com.google.android.gms.maps.model.MarkerOptions;
    import com.google.android.material.snackbar.Snackbar;

    import java.io.IOException;
    import java.util.List;
    import java.util.Locale;


    public class MainActivity extends AppCompatActivity
            implements OnMapReadyCallback,
            ActivityCompat.OnRequestPermissionsResultCallback{


        private GoogleMap mMap;
        private Marker currentMarker = null;

        private static final String TAG = "googlemap_example";
        private static final int GPS_ENABLE_REQUEST_CODE = 2001;
        private static final int UPDATE_INTERVAL_MS = 1000// 1초
        private static final int FASTEST_UPDATE_INTERVAL_MS = 500; // 0.5초


        // onRequestPermissionsResult에서 수신된 결과에서 ActivityCompat.requestPermissions를 사용한 퍼미션 요청을 구별하기 위해 사용됩니다.
        private static final int PERMISSIONS_REQUEST_CODE = 100;
        boolean needRequest = false;


        // 앱을 실행하기 위해 필요한 퍼미션을 정의합니다.
        String[] REQUIRED_PERMISSIONS  = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};  // 외부 저장소


        Location mCurrentLocatiion;
        LatLng currentPosition;


        private FusedLocationProviderClient mFusedLocationClient;
        private LocationRequest locationRequest;
        private Location location;


        private View mLayout;  // Snackbar 사용하기 위해서는 View가 필요합니다.
        // (참고로 Toast에서는 Context가 필요했습니다.)

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
                    WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

            setContentView(R.layout.activity_main);

            mLayout = findViewById(R.id.layout_main);

            locationRequest = new LocationRequest()
                    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
                    .setInterval(UPDATE_INTERVAL_MS)
                    .setFastestInterval(FASTEST_UPDATE_INTERVAL_MS);


            LocationSettingsRequest.Builder builder =
                    new LocationSettingsRequest.Builder();

            builder.addLocationRequest(locationRequest);


            mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);


            SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager()
                    .findFragmentById(R.id.map);
            mapFragment.getMapAsync(this);
        }

        @Override
        public void onMapReady(final GoogleMap googleMap) {
            Log.d(TAG, "onMapReady :");

            mMap = googleMap;

            //런타임 퍼미션 요청 대화상자나 GPS 활성 요청 대화상자 보이기전에
            //지도의 초기위치를 서울로 이동
            setDefaultLocation();



            //런타임 퍼미션 처리
            // 1. 위치 퍼미션을 가지고 있는지 체크합니다.
            int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION);
            int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_COARSE_LOCATION);



            if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED &&
                    hasCoarseLocationPermission == PackageManager.PERMISSION_GRANTED   ) {

                // 2. 이미 퍼미션을 가지고 있다면
                // ( 안드로이드 6.0 이하 버전은 런타임 퍼미션이 필요없기 때문에 이미 허용된 걸로 인식합니다.)


                startLocationUpdates(); // 3. 위치 업데이트 시작


            }else//2. 퍼미션 요청을 허용한 적이 없다면 퍼미션 요청이 필요합니다. 2가지 경우(3-1, 4-1)가 있습니다.

                // 3-1. 사용자가 퍼미션 거부를 한 적이 있는 경우에는
                if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])) {

                    // 3-2. 요청을 진행하기 전에 사용자가에게 퍼미션이 필요한 이유를 설명해줄 필요가 있습니다.
                    Snackbar.make(mLayout, "이 앱을 실행하려면 위치 접근 권한이 필요합니다.",
                            Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                        @Override
                        public void onClick(View view) {

                            // 3-3. 사용자게에 퍼미션 요청을 합니다. 요청 결과는 onRequestPermissionResult에서 수신됩니다.
                            ActivityCompat.requestPermissions( MainActivity.this, REQUIRED_PERMISSIONS,
                                    PERMISSIONS_REQUEST_CODE);
                        }
                    }).show();


                } else {
                    // 4-1. 사용자가 퍼미션 거부를 한 적이 없는 경우에는 퍼미션 요청을 바로 합니다.
                    // 요청 결과는 onRequestPermissionResult에서 수신됩니다.
                    ActivityCompat.requestPermissions( this, REQUIRED_PERMISSIONS,
                            PERMISSIONS_REQUEST_CODE);
                }

            }



            mMap.getUiSettings().setMyLocationButtonEnabled(true);
            mMap.animateCamera(CameraUpdateFactory.zoomTo(15));
            mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {

                @Override
                public void onMapClick(LatLng latLng) {

                    Log.d( TAG, "onMapClick :");
                }
            });
        }

        LocationCallback locationCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                super.onLocationResult(locationResult);

                List<Location> locationList = locationResult.getLocations();

                if (locationList.size() > 0) {
                    location = locationList.get(locationList.size() - 1);
                    //location = locationList.get(0);

                    currentPosition
                            = new LatLng(location.getLatitude(), location.getLongitude());


                    String markerTitle = getCurrentAddress(currentPosition);
                    String markerSnippet = "위도:" + String.valueOf(location.getLatitude())
                            + " 경도:" + String.valueOf(location.getLongitude());

                    Log.d(TAG, "onLocationResult : " + markerSnippet);


                    //현재 위치에 마커 생성하고 이동
                    setCurrentLocation(location, markerTitle, markerSnippet);

                    mCurrentLocatiion = location;
                }


            }

        };



        private void startLocationUpdates() {

            if (!checkLocationServicesStatus()) {

                Log.d(TAG, "startLocationUpdates : call showDialogForLocationServiceSetting");
                showDialogForLocationServiceSetting();
            }else {

                int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_FINE_LOCATION);
                int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                        Manifest.permission.ACCESS_COARSE_LOCATION);



                if (hasFineLocationPermission != PackageManager.PERMISSION_GRANTED ||
                        hasCoarseLocationPermission != PackageManager.PERMISSION_GRANTED   ) {

                    Log.d(TAG, "startLocationUpdates : 퍼미션 안가지고 있음");
                    return;
                }


                Log.d(TAG, "startLocationUpdates : call mFusedLocationClient.requestLocationUpdates");

                mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, Looper.myLooper());

                if (checkPermission())
                    mMap.setMyLocationEnabled(true);

            }

        }


        @Override
        protected void onStart() {
            super.onStart();

            Log.d(TAG, "onStart");

            if (checkPermission()) {

                Log.d(TAG, "onStart : call mFusedLocationClient.requestLocationUpdates");
                mFusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null);

                if (mMap!=null)
                    mMap.setMyLocationEnabled(true);

            }


        }


        @Override
        protected void onStop() {

            super.onStop();

            if (mFusedLocationClient != null) {

                Log.d(TAG, "onStop : call stopLocationUpdates");
                mFusedLocationClient.removeLocationUpdates(locationCallback);
            }
        }




        public String getCurrentAddress(LatLng latlng) {

            //지오코더... GPS를 주소로 변환
            Geocoder geocoder = new Geocoder(this, Locale.getDefault());

            List<Address> addresses;

            try {

                addresses = geocoder.getFromLocation(
                        latlng.latitude,
                        latlng.longitude,
                        1);
            } catch (IOException ioException) {
                //네트워크 문제
                Toast.makeText(this, "지오코더 서비스 사용불가", Toast.LENGTH_LONG).show();
                return "지오코더 서비스 사용불가";
            } catch (IllegalArgumentException illegalArgumentException) {
                Toast.makeText(this, "잘못된 GPS 좌표", Toast.LENGTH_LONG).show();
                return "잘못된 GPS 좌표";

            }


            if (addresses == null || addresses.size() == 0) {
                Toast.makeText(this, "주소 미발견", Toast.LENGTH_LONG).show();
                return "주소 미발견";

            } else {
                Address address = addresses.get(0);
                return address.getAddressLine(0).toString();
            }

        }


        public boolean checkLocationServicesStatus() {
            LocationManager locationManager = (LocationManager) getSystemService(LOCATION_SERVICE);

            return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
                    || locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        }


        public void setCurrentLocation(Location location, String markerTitle, String markerSnippet) {


            if (currentMarker != null) currentMarker.remove();


            LatLng currentLatLng = new LatLng(location.getLatitude(), location.getLongitude());

            MarkerOptions markerOptions = new MarkerOptions();
            markerOptions.position(currentLatLng);
            markerOptions.title(markerTitle);
            markerOptions.snippet(markerSnippet);
            markerOptions.draggable(true);


            currentMarker = mMap.addMarker(markerOptions);

            CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLng(currentLatLng);
            mMap.moveCamera(cameraUpdate);

        }


        public void setDefaultLocation() {


            //디폴트 위치, Seoul
            LatLng DEFAULT_LOCATION = new LatLng(37.56, 126.97);
            String markerTitle = "위치정보 가져올 수 없음";
            String markerSnippet = "위치 퍼미션과 GPS 활성 요부 확인하세요";


            if (currentMarker != null) currentMarker.remove();

            MarkerOptions markerOptions = new MarkerOptions();
            markerOptions.position(DEFAULT_LOCATION);
            markerOptions.title(markerTitle);
            markerOptions.snippet(markerSnippet);
            markerOptions.draggable(true);
            markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
            currentMarker = mMap.addMarker(markerOptions);

            CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(DEFAULT_LOCATION, 15);
            mMap.moveCamera(cameraUpdate);

        }


        //여기부터는 런타임 퍼미션 처리을 위한 메소드들
        private boolean checkPermission() {

            int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_FINE_LOCATION);
            int hasCoarseLocationPermission = ContextCompat.checkSelfPermission(this,
                    Manifest.permission.ACCESS_COARSE_LOCATION);



            if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED &&
                    hasCoarseLocationPermission == PackageManager.PERMISSION_GRANTED   ) {
                return true;
            }

            return false;

        }



        /*
        * ActivityCompat.requestPermissions를 사용한 퍼미션 요청의 결과를 리턴받는 메소드입니다.
        */
        @Override
        public void onRequestPermissionsResult(int permsRequestCode,
                                              @NonNull String[] permissions,
                                              @NonNull int[] grandResults) {

            if ( permsRequestCode == PERMISSIONS_REQUEST_CODE && grandResults.length == REQUIRED_PERMISSIONS.length) {

                // 요청 코드가 PERMISSIONS_REQUEST_CODE 이고, 요청한 퍼미션 개수만큼 수신되었다면

                boolean check_result = true;


                // 모든 퍼미션을 허용했는지 체크합니다.

                for (int result : grandResults) {
                    if (result != PackageManager.PERMISSION_GRANTED) {
                        check_result = false;
                        break;
                    }
                }


                if ( check_result ) {

                    // 퍼미션을 허용했다면 위치 업데이트를 시작합니다.
                    startLocationUpdates();
                }
                else {
                    // 거부한 퍼미션이 있다면 앱을 사용할 수 없는 이유를 설명해주고 앱을 종료합니다.2 가지 경우가 있습니다.

                    if (ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[0])
                            || ActivityCompat.shouldShowRequestPermissionRationale(this, REQUIRED_PERMISSIONS[1])) {


                        // 사용자가 거부만 선택한 경우에는 앱을 다시 실행하여 허용을 선택하면 앱을 사용할 수 있습니다.
                        Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 앱을 다시 실행하여 퍼미션을 허용해주세요. ",
                                Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                            @Override
                            public void onClick(View view) {

                                finish();
                            }
                        }).show();

                    }else {


                        // "다시 묻지 않음"을 사용자가 체크하고 거부를 선택한 경우에는 설정(앱 정보)에서 퍼미션을 허용해야 앱을 사용할 수 있습니다.
                        Snackbar.make(mLayout, "퍼미션이 거부되었습니다. 설정(앱 정보)에서 퍼미션을 허용해야 합니다. ",
                                Snackbar.LENGTH_INDEFINITE).setAction("확인", new View.OnClickListener() {

                            @Override
                            public void onClick(View view) {

                                finish();
                            }
                        }).show();
                    }
                }

            }
        }


        //여기부터는 GPS 활성화를 위한 메소드들
        private void showDialogForLocationServiceSetting() {

            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            builder.setTitle("위치 서비스 비활성화");
            builder.setMessage("앱을 사용하기 위해서는 위치 서비스가 필요합니다.\n"
                    + "위치 설정을 수정하실래요?");
            builder.setCancelable(true);
            builder.setPositiveButton("설정", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int id) {
                    Intent callGPSSettingIntent
                            = new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                    startActivityForResult(callGPSSettingIntent, GPS_ENABLE_REQUEST_CODE);
                }
            });
            builder.setNegativeButton("취소", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int id) {
                    dialog.cancel();
                }
            });
            builder.create().show();
        }


        @Override
        protected void onActivityResult(int requestCode, int resultCode, Intent data) {
            super.onActivityResult(requestCode, resultCode, data);

            switch (requestCode) {

                case GPS_ENABLE_REQUEST_CODE:

                    //사용자가 GPS 활성 시켰는지 검사
                    if (checkLocationServicesStatus()) {
                        if (checkLocationServicesStatus()) {

                            Log.d(TAG, "onActivityResult : GPS 활성화 되있음");


                            needRequest = true;

                            return;
                        }
                    }

                    break;
            }
        }



    }




    5. 관련 포스팅


    5.1. Google Maps Android API 사용 방법 및 예제

    Google Maps Android API를 사용하는 기본적인  방법과 사용시 발생할 수 있는 문제점에 대해 다룹니다.

    http://webnautes.tistory.com/647 




    5.2. Places API Web Service를 사용하여 Android Google Map에 현재 위치 주변의 음식점 표시하기


    Places API Web Service를 이용하여 현재 위치 주변의 음식점 정보를 안드로이드의 구글맵에 표시하는 내용을 다룹니다.

    http://webnautes.tistory.com/1080 



    5.3. GenyMotion 가상머신에 Google Apps설치하여 Google Maps Android API 테스트 하기


    Genymotion에서 Google Map API을 가지고 작성한 Android 앱을 테스트하는 방법을 소개합니다.

    http://webnautes.tistory.com/1064 





    포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
    댓글로 알려주시면 빠른 시일내에 답변을 드리겠습니다.

    여러분의 응원으로 좋은 컨텐츠가 만들어집니다. 지금 본 내용이 도움이 되었다면 유튜브 구독 부탁드립니다. 감사합니다 : )

    유튜브 구독하기


    댓글 114

    • 이전 댓글 더보기
    • Hanieum 2019.07.14 19:40


      위에 구글맵을 보면 자신의 위치를 가져오는 버튼이 있는데
      보고 따라했는데 버튼이 생성안되고 GPS를 설정해도 그대로 DEFAULT_LOCATION의 위치가 나오네요...
      어떻게 설정을해야 자신의 위치를 가져와야되나요?

    • Favicon of https://webnautes.tistory.com/1249 BlogIcon egdg 2019.08.05 18:11


      안녕하세요! 블로그 포스팅 보고 현재 위치를 잡아오는 건 성공했습니다. 그런데 마커에 있는 그 정보를 다른 엑티비티로 넘겨주어 그 위치정보를 다른 엑티비티에 적으려면 어떻게 해야할까요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.08.05 18:31 신고


        마커 클릭시 Intent를 사용하여 넘겨주면 됩니다.

        마커 클릭시 호출되는 다음 함수를 사용하세요.

        GoogleMap.OnMarkerClickListener

    • Favicon of https://famasofbyeolnae.tistory.com BlogIcon 별내동파마스 2019.08.30 14:07 신고


      안녕하세요 유튜브와 티스토리 글 잘 보고있는 학부생입니다.
      올려주신 코드를보고 실행결과 제 위치도 잘 잡습니다. 감사합니다.
      질문이 하나있는데.
      제가 배달의 민족처럼 현재위치를 잡아놓은 상태에서 제 주소로 설정이라고 하면
      주소가 설정되어 그 위도 경도 값을 받아서 파라미터로 저장후 위도 경도 값으로 가게를 거리순 정렬해주고싶습니다.
      가게의 경우는 제가 임의로 DB에 입력할것입니다. 이 때 가게와 사용자 위치를 비교하기위해
      가게 등록시에도 위도 경도값을 받아야할까요? 그리고 위도 경도값은 위 코드에서 어디서 가져오는지 알고싶습니다!

      String markerTitle = getCurrentAddress(currentPosition);
      String markerSnippet = "위도:" + String.valueOf(location.getLatitude())
      + " 경도:" + String.valueOf(location.getLongitude());

      이부분에서처럼 getLatitude와 getLongitude 메소드를 이용해서 따로 파라미터를 저장해야할까요?

      현 화면에서 버튼하나 주어 현재 위치로 설정 을 눌러서 제 위치로 설정하고싶습니다.

    • ssdfg 2019.09.17 21:24


      안녕하세요 깔끔한 강의 글 감사합니다.

      워낙 쉽게 설명해 주신 터라 조금씩 욕심이 생겨 한가지 기능을 직접 만들어 넣어 보려고 했는데

      작동이 되질 않아 처음으로 질문 글 올립니다.

      private static final int UPDATE_INTERVAL_MS = 1000; // 1초에 한번씩


      locationRequest = new LocationRequest()
      .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) // 이러한 품질로
      .setInterval(UPDATE_INTERVAL_MS) // 업데이트 시킨다.
      .setFastestInterval(FASTEST_UPDATE_INTERVAL_MS);


      여기서 말하는 업데이트의 내용은

      private void startLocationUpdates() {

      해당 부분을 갱신 한다는 의미인가요?

      아니면 단순하게 화면을 갱신 시킨다는 의미인가요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.09.17 21:52 신고


        startLocationUpdates를 호출하면 현재 위치를 가져오는 것을 시작하게 됩니다.

        .setInterval(UPDATE_INTERVAL_MS)에서 지정한 시간 간격대로 onLocationResult가 호출되어 현재 위치를 가져옵니다.

    • 김김김 2019.10.13 22:44


      안녕하세요! 이 글 보고 막혀있던게 조금 풀렸습니다 정말 감사합니다!
      한가지 질문이 생겨서 여쭤봅니다.

      처음 화면에서 마커가 현재위치를 찍어줬을 때 제가 임의로 위치를 이동시키면 해당하는 위치로 현재 위치를 등록시키고 싶은데 어떻게 해야될까요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.14 14:42 신고


        GPS에서 얻은 위치로 이동하지 않도록 다음 부분이 실행되지 않게 처리해주면 될듯합니다.

        //현재 위치에 마커 생성하고 이동
        setCurrentLocation(location, markerTitle, markerSnippet);

    • 김성 2019.10.14 18:37


      저는 이글 보고 코드를 있는 그대로 계속 따라하는 데도 자꾸 미국쪽으로 지도가 나와서...
      무슨 문제가 있는건가요?

    • 김현철 2019.10.19 18:29


      질문 두개만 더 드리려고요..

      구글지도에 내위치를 표시하고 마커를 수백개를 만들었는데요.. 실행하면 길이가 길다는 에라가 나옵니다..
      지금은 그냥 001, 002, 003 이렇게 마커를 하나하나 다 수작업으로 입력한거거든요..
      이걸좀 다르게 할수있는방법이 있을거 같아서요..

      그리고, 마커 옵션에 .snippet
      줄수를 한줄이 아닌 여러줄로 하려면 어떻게 해야 하나요 ?

      찾아보니,
      마크옵션에는 배열을 쓴다던가..
      부제에는 에디트텍스트 쓰면 여러줄로 됀다고해서,
      유튜브 보고 여러번 시도를 해봣는데.. 에러만 나서요..
      어떻게 써야하는지를 모르겠습니다..

      부탁 좀 드리겠습니다..

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.19 20:06 신고


        찾아보니 다음처럼 \n을 넣으면 여러 줄이 된다합니다.

        snippet("My Snippet"+"\n"+"1st Line Text"+"\n"+"2nd Line Text"+"\n"+"3rd Line Text")


        첫번째 질문은 무슨 의미인지 이해가 안갑니다..^^;

    • 김현철 2019.10.19 20:40


      감사합니다.. 항상 도움만 받고있네요..

      첫번째 질문은..
      마커를 한 500개정도가 필요해서요.. 근데 방법을 몰라서요..

      MarkerOptions mark263 = new MarkerOptions();
      mark263.position(new LatLng(37.513358, 126.940058))
      .title("노량진역")
      .snippet("수산시장근처.");
      BitmapDrawable bitmapdraw263=(BitmapDrawable)getResources().getDrawable(R.drawable.img3);
      Bitmap b263=bitmapdraw263.getBitmap();
      Bitmap smallMarker263 = Bitmap.createScaledBitmap(b263, 110, 110, false);
      mark263.icon(BitmapDescriptorFactory.fromBitmap(smallMarker263));
      mark263.alpha(.5f);
      mMap.addMarker(mark263);

      MarkerOptions mark264 = new MarkerOptions();
      mark264.position(new LatLng(37.563619, 127.004489))
      .title("동대입구역");
      BitmapDrawable bitmapdraw264=(BitmapDrawable)getResources().getDrawable(R.drawable.img3);
      Bitmap b264=bitmapdraw264.getBitmap();
      Bitmap smallMarker264 = Bitmap.createScaledBitmap(b264, 110, 110, false);
      mark264.icon(BitmapDescriptorFactory.fromBitmap(smallMarker264));
      mark264.alpha(.5f);
      mMap.addMarker(mark264);

      요렇게 번호를 매겨서 수작업으로 넣었거든요..
      이거를 좀 다른방법으로 할수있지않을까 해서요..
      실행하니까..

      .... to long 이라는 에러가 떠요.. override 가 너무 길다구요..

    • gyungjja 2019.10.23 12:45


      안녕하세요! 제공해주신 소스로 한번 실행을 해보았습니다.
      애뮬레이터상에서는 별다른 문제가 없는것 같습니다.
      하지만 실제 핸드폰으로 apk를 추출하여 실행을 해보았는데 지오코더 사용 불가라고 하는데 어떻게 해야하나요??

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.23 13:04 신고


        일시적으로 그런건지 계속 그런건지 장소를 바꾸어가며 해보세요.

      • gyungjja 2019.10.23 13:06


        아 넵넵 방금은 주소 미발견이라고 뜨는데 이거는 어떻게 해결해야되나요?? 이 문제도 장소를 계속 바꾸어가면서 해야되는건가요 ?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.23 18:45 신고


        GPS 값에 따라 제대로 동작하는 경우도 있고 주소를 못찾는 경우도 있습니다.

        제대로 된 경우만 보여주기 위해 해당 메시지를 안보이게 하면 될 듯합니다.

    • 빈츠학살자 2019.11.05 00:09


      안녕하세요! 제가 글을 보고 따라 했는데 한가지 질문이 있어서 여쭤봅니다.
      제가 구글 지도랑 서치뷰와 연동 중인데
      처음에만 현재위치에 있고 검색하면 다시 현재위치로 안오고 검색한 위치에서 있고 싶은데 어떻게 해야할까요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.05 00:30 신고


        다음 부분이 일시적으로 실행안되게 막으세요.

        //현재 위치에 마커 생성하고 이동
        setCurrentLocation(location, markerTitle, markerSnippet);

        mCurrentLocatiion = location;


        현재 위치에 마커 찍는 것도setCurrentLocation 함수가 합니다.

      • 빈츠학살자 2019.11.05 14:15


        LocationCallback 부분안에 있는 건데 LocationCallback이 다른 부분에서도 사용되는 거 같아서 저부분만 일시적으로 실행 안되게 어떻게 해야하는지 모르겠네요 혹시 stop()이나 finish()를 사용해야 하는 건가요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.05 19:31 신고


        상황에따라 사용을 막을수 있게 불타입 변수를 추가해서 하세요

      • 빈츠학살자 2019.11.05 20:45


        그러면 불타입 변수를 선언하고 현재위치 값이 맞으면 그 코드가 실행되게 하면 되는건가요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.05 23:15 신고


        검색했다면 setCurrentLocation 코드가 실행안되게 하면 되겠죠.

      • 빈츠학살자 2019.11.06 16:09


        검색하면 setCurrentLocation을 실행 안되게 하라고 하셨는데 제가 현재위치는 onMapReady안에 있고 서치뷰 검색은 onCreate안에 있는데 여기서 막혀서 어떻게 방향을 잡아야 할지 모르겠습니다. 혹시 알려주실 수 있나요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.06 21:38 신고


        전역변수를 사용해보세요.

    • xxzx 2019.11.15 10:48


      코드 너무 감사합니다~~
      저는 주기적으로 제 위치가 기록되고, 백그라운드에서도 몇일간 오랫동안 실행되기(현재위치가 주기적으로 기록)를 원합니다.
      코드상에는 1초마다 위치가 기록되는 것으로 나오는데 기록된 위치는 어디서 어떻게 볼 수 있을까요?
      또한, 백그라운드에서 실행되기 하기 위해서는 어떻게 코드를 수정해야 할까요? 몇일간 오래 지속되기를 원합니다. (최소 하루 이상)

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.16 18:04 신고


        setCurrentLocation 호출하는 부분이 현재위치를 얻는 부분입니다. 액티비티를 서비스로 바꿔야하지 않을까 싶습니다

      • 키키도르 2019.11.24 15:03


        setCurrentLocation 호출하는 부분이

        LocationCallback locationCallback = new LocationCallback() {
        @Override
        public void onLocationResult(LocationResult locationResult) {
        super.onLocationResult(locationResult);

        List<Location> locationList = locationResult.getLocations();

        if (locationList.size() > 0) {
        location = locationList.get(locationList.size() - 1);
        //location = locationList.get(0);

        currentPosition = new LatLng(location.getLatitude(), location.getLongitude());


        String markerTitle = getCurrentAddress(currentPosition);
        String markerSnippet = "위도:" + String.valueOf(location.getLatitude())
        + " 경도:" + String.valueOf(location.getLongitude());

        Log.d(TAG, "onLocationResult : " + markerSnippet);


        //현재 위치에 마커 생성하고 이동
        setCurrentLocation(location, markerTitle, markerSnippet);

        mCurrentLocatiion = location;
        }


        }

        };

        이 부분 말씀하시는 건가요? 이 엑티비티만 서비스로 바꾸면 백그라운드에서도 실행이 유지되며 작동이 이어서 할까요??

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.24 16:48 신고


        깃허브에서 서비스로 바꾼 예제 프로젝트를 찾아보세요.

      • 2019.11.25 14:01


        비밀댓글입니다

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.25 21:34 신고


        service를 사용해본적이 없어서 저도 개념이 약함니다^^;

    • 천재 2019.11.15 15:30


      코틀린으로 바꿔서 코딩중인데요
      override fun onRequestPermissionsResult(permsRequestCode: Int,
      @NonNull permissions: Array<String?>?,
      @NonNull grantResults: IntArray) 의 override 부분에서 밑줄이 뜹니다ㅠㅠ 어떻게 해결해야할까요

    • 창창 2019.11.21 14:55


      안녕하세요. 정말 절박한 심정으로 글을 적게 되었습니다. 제가 전에 mapview를 이용해서 fragment에 구글지도를 띄웠을때는 잘 되었는데용. androidx로 바뀌고 나서 mapview를 fragment에 적용하니까 지도가 제대로 보이지 않는 현상이 일어났습니다. 축소를 하면 자세한 정보가 나와야 되는데 줌만 축소되고 구체적인 지형정보들은 나타나지 않게 되었습니다. fragment에 구글지도를 넣는 방법을 다 해봤지만 해결방법을 못찾아서 이렇게 질문드리게 되었습니다... androidx에서 구글지도를 fragment안에 넣는 것이 가능한것인가요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.21 21:33 신고


        androidx와 android.support는 패키지 이름만 다른 뿐 똑같은 코드로 동작하는 것으로 알고 있습니다. 동작 안되는 원인까지는 제가 알지 못하네요...

    • 신신 2019.11.22 22:21


      안녕하세요. 처음부터 내위치로 찍히게 하는 방법은 없을까요?
      setDefaultLocation 부분을 주석하고 실행하면 지도가 아예 안뜨거나 하고 if문으로 gps위치를 찾지 못한경우에는 디폴트로 넘어가고 아닌경우에는 setCurrentLocation으로 넘어가게끔 했는데 무조건 디폴트쪽으로만 되네요ㅜ 해결방안이 있을까요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.22 23:01 신고


        처음에는 현재 위치에 대한 지도가 보일때 까지 오래 걸릴 가능성이 높기 때문에 setDefaultLocation 는 무조건 호출해줘야 합니다.

    • 감사합니당 2019.11.25 21:32


      안녕하세요. 졸업프로젝트 진행중인 학생입니다. 비전공자로서 어플을 만드는데 많이 도움을 받고있습니다.
      정말 감사합니다..
      다름이 아니라, 구글맵을 처음 실행했을 때 찍히는 현재 위치를 고정적으로 받고싶은데 올려 주신 코드는 현재 위치가 실시간으로 바뀌더라구요.
      응용해보려고 이것 저것 다 해봤는데 어렵네요..
      처음 찍히는 현재 위치만을 받아보려면 어느 메소드를 삭제해야 할까요... 답변 부탁드립니다. 감사합니다.

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.25 21:43 신고


        locationCallback 의 아래 부분이 변경된 현재 위치로 이동하는 코드입니다.

        //현재 위치에 마커 생성하고 이동
        setCurrentLocation(location, markerTitle, markerSnippet);

    • 2019.12.05 13:46


      비밀댓글입니다

    • 이상락 2019.12.15 00:26


      안녕하세요 제가 지금 지도 어플 프로젝트로 하고 있는데 추가하고 싶은 기능이 있는데 선생님께서 만든 어플을 보면 사진 상단에 동그라미 버튼 같은것이 있는데 그걸 누를시 현재위치에 파랑색 점이 뜨면서 현재위치로 이동하는 것이 있는거 같은데 그것은 코드 어느 부분인지 알 수 있을까요??

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.12.15 07:20 신고


        아래 링크를 참고하세요

        http://www.masterqna.com/android/54015/%EA%B5%AC%EA%B8%80%EB%A7%B5%EC%9D%98-%ED%98%84%EC%9E%AC-%EC%9C%84%EC%B9%98-%EB%B2%84%ED%8A%BC%EC%9D%98-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%40

    • 여기짱이야 2020.01.15 15:03


      안녕하세요
      선생님 덕분에 정말 많은 도움을 받고 있습니다.
      한가지 궁금한게 있는데 현재위치 마커 말고 파란색 동그라미로 된 이미지는 어떻게 생겨난건가요?
      저 이미지를 바꾸어주고 싶은데 setCurrentLocation일까 싶어서 주석처리를 하고 실행했는데 아니더라구요

    • ㅇㅇ 2020.01.18 00:01


      안녕하세요.
      글로 도움이 참 많이 되었습니다.

      의문이 생긴 곳이 있는데 LocationSettingsRequest.Builder builder는 왜 선언을 해두고 build하는 부분이 없나요?
      감사합니다

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.01.18 08:30 신고


        다음처럼만 하면 동작합니다.

        LocationSettingsRequest.Builder builder =
        new LocationSettingsRequest.Builder();

        builder.addLocationRequest(locationRequest);

    • HIT 2020.02.08 15:53


      에뮬에서 할 때 DMS로 가상좌표(?) 넣어주는게 있다던데 그거 사용하는 예제는 없으신가요?
      에뮬에서 하니까 구글 본사로 지도가 나오네요 ㅠㅠ

Designed by Tistory.