반응형


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


2018. 10.2

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

2019. 8. 15  SupportMapFragment로 변경

2020. 9. 12 테스트 (  Android Studio 4.0.1, Android 10.0+ (R)  API 30 )



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


Android Studio 4.0.1

Android 10.0+ (R)  API 30





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> 태그를 사용하여 위치정보 접근을 위한 퍼미션을 추가해줍니다.



<?xml version="1.0" encoding="utf-8"?>

<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를 추가해야 합니다. 

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


dependencies {

    implementation fileTree(dir: "libs", include: ["*.jar"])

    implementation 'androidx.appcompat:appcompat:1.2.0'

    implementation 'androidx.constraintlayout:constraintlayout:2.0.1'

    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.3.0-alpha02'

    testImplementation 'junit:junit:4.12'

    androidTestImplementation 'androidx.test.ext:junit:1.1.2'

    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

}




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 





반응형

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

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

유튜브 구독하기


제가 쓴 책도 한번 검토해보세요.

  1. 이전 댓글 더보기
  2. asdf 2020.04.24 14:23

    안녕하세요. 항상 자세하고 친절한 코드 잘 보고있습니다.
    이번 포스팅도 제가 잘 따라하고있습니다.
    그런데 혹시 여기서 추가적으로 버튼을 눌렀을 때 마커가 찍히도록 하려면 어떻게해야하는지 조언 가능할까요?
    setOnClickListener 안에 어떻게 넣어야하는지, 그리고 한 번만 찍히는게 아니라 제가 다시 버튼을 누를 때까지 마커가 찍히도록 하고싶습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.24 19:07 신고

      현재 위치에 마커를 추가하는 건가요? 아님 원하는 위치를 터치해서 추가하는 건가요?

    • asdf 2020.04.24 19:44

      현재 위치에 자동으로 5초 간격으로 마커를 찍으려 합니다. 지금 이 코드를 가지고 앱을 켜자마자 5초 간격으로 마커를 찍고는 있는데... 저는 그걸 버튼을 눌렀을 때 실행하고 싶습니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.24 19:48 신고

      버튼 누를때마다 setCurrentLocation 함수 호출할지 여부를 플래그변수로 조정하면 됩니다

    • asdf 2020.04.24 19:57

      아 감이 오네요! 감사합니다!!

    • asdf 2020.04.24 22:54

      이렇게 구현했더니 눌렀을 때 시작해서 잘 돌아가는데요,
      startBtn.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
      walkState++;
      if(walkState % 2 == 0) {
      Toast.makeText(getApplicationContext(), "걸음 시작", Toast.LENGTH_SHORT).show();
      startBtn.setText("걸음 정지");
      startLocationUpdates();
      } else if(walkState % 2 == 1) {
      Toast.makeText(getApplicationContext(), "걸음 정지", Toast.LENGTH_SHORT).show();
      startBtn.setText("걸음 시작");

      }
      }
      });

      다시 눌렀을 때 메소드 어떻게 종료시켜야할까요 ㅠ
      아니면 이런식이 아닌가요;;

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.24 23:04 신고

      setCurrentLocation을 호출해야 마커가 화면에 출력됩니다. 필요할때 이 함수를 호출하고 필요없을땐 호출안하는게 더 나을듯합니다.

  3. 앱개발초보 2020.04.27 01:21

    구글맵을 이용해서 검색하여 목적지를 선정해 마커로 지정해놓고(길찾기는 필요없습니다) 현재 위치가 움직여 지정된 마커에 겹쳐진다면(목적지에 도달) 알림이 오게 할 수 있을까요?
    그리고 이 동작들을 백그라운드에서도 실행 가능할까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.27 06:24 신고

      아래 링크를 참고하세요. 백그라운드 실행하려면 깃허브에서 service로 바꾼 코드를 찾아보세요
      https://webnautes.tistory.com/1165

    • 앱개발초보 2020.04.27 13:33

      Genymotion이 애뮬레이터던데 따라하면 실제 앱에서도 쓸수있는건가요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.27 14:39 신고

      네 맞습니다

  4. 안녕하세요 2020.04.27 01:21

    안녕하세요! 제가 공기계로 테스트를 해봤는데 코드랑 다 복사해서 넣었거든요 근데 계속 지도가 뜨질않아요ㅠㅠ 키 문제라고 생각하고 키를 몇번이나 다시 만들어서 해봤는데도 되질않네요ㅠㅠ 제 공기계 안드로이드 버전이 5.0.1 인데 이것과도 상관이 있을까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.27 06:25 신고

      로그캣을 확인해보셔야 합니다. 안드로이드 버전은 상관없을듯합니다.

  5. ㅇㅇ 2020.04.27 20:45

    혹시 버튼 눌렀을 때 gps 멈추는 방법 있을까요? onPause가 안 돼서요.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.27 20:49 신고

      onStop 메소드가 실행되면 위치 업데이트가 중지되고

      onStart 메소드가 실행되면 위치 업데이트가 시작됩니다.

      두 메소드에 있는 코드를 참고하여 구현하시면 될듯합니다.

  6. help 2020.04.27 22:05

    안녕하세요! 제가 이 블로그를 참고하여 구글맵에 이동경로를 보여주는 앱을 만들고있는데 polylineOptions를 사용하여 ArrayList에 담아 이동경로를 보여주고있습니다. 버튼을 누르면 ArrayList에 clear()를 해서 폴리라인이 없어지는데 새 위치를 받아오면 지워졌던 폴리라인이 다시 생기더라구요... 혹시 이 부분에 대해서 어떤 문제인지 아시나요? ㅠㅠ

    • 2020.04.27 22:06

      비밀댓글입니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.04.27 23:10 신고

      좌표가 아니라 polylineOptions를 arraylist에 저장해야 하네요

      아래 링크를 참고하세요

      https://stackoverflow.com/a/14861563

  7. Kyu 2020.05.03 05:15

    앱을 실행하면 맵이 보여야할 부분이
    그냥 비어있어요 ㅠㅠ 어떻게 해야할까요?
    다른 현재위치 구글맵들 사용했을때도 이랬어요 ㅠ

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.03 07:58 신고

      인증 오류같습니다. 키 발급받을때 사용한 패키지이름돠 현재 앱의 패키지 이름이 같나 확인해보세요

  8. dsds 2020.05.11 14:37

    지금 현재 위치버튼 이벤트가 오른쪽 맨 위 상단 끝에 있는데 밑으로 내릴 수 있는 방법이 있을까요

  9. 안녕하세요 2020.05.12 03:11

    안녕하세요 캡스톤디자인 진행중인 학생입니다. 올려주신 강의를 보면서 열심히 참고하고있습니다 감사합니다. cameraFactory.newLatLng(currentLatLng)부분을 주석화시켜서 강제로 카메라가 현재위치로 이동하는 부분을 해제해주었는데 이걸 해제하니 현재위치가 잡혀도 현재위치로 자동이동을 안하더라구요 초기에 위치가 잡혔을때 1번만 default위치에서 현재위치로 자동이동되게하려면 어떻게 수정해야할까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.12 06:18 신고

      현재위치로 이동한후 카메라 이동을 중지시키면 될듯합니다

  10. 크롱짱 2020.05.17 16:22

    안녕하세요. 코드 잘 보았습니다.
    비전공자인데 도움이 많이 되었어요.

    다름이 아니라 이 코드 전문을 그대로 사용하되, 구글 지도에 마크를 여러개 표현하고 싶은데 어떻게 하면 될까요??

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.17 16:24 신고

      setCurrentLocation를 사용하여 원하는 위치에 마커를 추가하면 됩니다

  11. Favicon of https://metamong94.tistory.com BlogIcon 따라쟁이메타몽 2020.05.31 16:29 신고

    실행하면 아프리카 대륙은 아니더라도 미국대륙이 보이는데 이 때 어떻게 해야 하나요 ???

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.05.31 16:31 신고

      에뮬레이터라면 GPS 수신을 못받으므로 미국이 기본위치로 보입니다. 실제폰이라면 gps 수신이 가능한 환경에서 테스트해보세요

  12. illustb 2020.06.03 21:45

    안녕하세요. 위치기반 어플을 만들며 공부중인 학생입니다.
    다름이 아니고 구글맵에 찍히는 마커들을 이용하여 누적 이동거리를 구해보고 싶은데 어떤식으로 접근을 하면 될까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.06.03 21:49 신고

      데이터베이스 같은 곳에 마커 위치들을 기록했다가 누적 거리를 계산하면 좋을듯 합니다

  13. 2020.06.07 17:59

    비밀댓글입니다

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

      지도관련 레이라웃 관련 이름이 자바 파일에 잘못 적힌듯합니다. 포스트와 작성한 코드 사이에 다른 점을 확인해보세요

  14. ehdwnstls12 2020.08.24 07:36

    앞선 예제인 구글 맵API를 이용해 서울위치 찍기는 정상적으로 작동 하였고 다음 단계인 현재위치 찍어주는 내용을 따라 하였는데 현재위치가 정확히 잡히지 않습니다.
    설정에서 위치정보 를 on 하였고 메니페스트에 권한 설정도 다 하였습니다. 에뮬레이터에서는 구글 본사좌표로 찍히고 , 녹스로 실행하면 서울특별시 위치가 잡힙니다. 또한 우측 상단의 GPS 버튼효과는 가능한건지 궁금합니다.

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.08.24 21:08 신고

      안드로이드 스튜디오에 포함된 에뮬레이터에서는 GPS를 지정해줄 수 없어서 디폴트 값인 구글 본사 좌표가 출력됩니다.

      녹스는 사용해보지 않아서 정확히 모르겠군요

      GPS 버튼은 실제 안드로이드폰에서는 동작하는데 에뮬레이터에선 어떨지 모르겠습니다.


      Genymotion이 구글맵 테스트하기 좋았는데 너무 오래전에 해본거라 지금은 어떨지 모르겠군요

      GenyMotion 가상머신에 Google Apps설치하여 Google Maps Android API 테스트 하기
      https://webnautes.tistory.com/1064

  15. 초보자 2020.09.17 18:19

    혹시 하루동안 자기가 어디를 갔는지 지도에 찍히게 하려면 어떻게 해야할까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.17 21:02 신고

      하루동안 이동한 지점들을 DB같은데에 저장했다가 보여주는게 어떨가 싶습니다.

      모든 지점을 저장하면 양이 많으니 줄이는 방법도 생각해봐야 할듯합니다.

  16. IT마스터를 위하여 2020.09.22 00:25

    안녕하세요
    항상 도움 많이 받고 있습니다.
    한가지 궁금한게 있는데 처음에만 현재 위치를 찍어주고 그다음부터 카메라를 옮길때는 업데이트가 되지 않기 위해서는 어떻게 해야할까요?

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.22 07:40 신고

      setCurrentLocation를 주석처리하면 현재위치로 이동하지 않삽니다. 손으로 터치한경우 setCurrentLocation가 동작하지 않다가 현재 위치버튼누르면 다시 움직이도록 해도 될듯합니다

  17. Favicon of https://mystation.tistory.com BlogIcon 뽀끄니 2020.09.28 03:32 신고

    E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.cyberstation.busservice, PID: 28459
    java.lang.NullPointerException: Attempt to invoke virtual method 'com.google.android.gms.maps.model.Marker com.google.android.gms.maps.GoogleMap.addMarker(com.google.android.gms.maps.model.MarkerOptions)' on a null object reference
    at com.cyberstation.busservice.MainActivity.setDefaultLocation(MainActivity.java:433)
    at com.cyberstation.busservice.MainActivity.onMapReady(MainActivity.java:170)

    ERROR 나는군요 왜그럴까요

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.28 19:51 신고


      다음 부분에서 mMap 또는 markerOptions 가 문제인듯합니다.
      혹시 포스트와 다르게 수정한 부분이 있나요?

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


      currentMarker = mMap.addMarker(markerOptions);

  18. 2020.09.28 16:38

    비밀댓글입니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.28 19:52 신고

      자바코드 파일당 액티비티(클래스)는 하나씩 포함되어야 합니다.

      파일이름과 액티비티(클래스)이름도 일치해야합니다.

      질문을 맞게 이해했나 모르겠군요

  19. 2020.09.29 00:15

    비밀댓글입니다

    • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2020.09.29 08:24 신고

      지도는 보이나요? 앱을 삭제하고 재설치해서 퍼미션 물어보는게 아니면 지도 액티비티가 실행되는데 문제가 있어보입니다

  20. 이은규 2020.10.10 21:17

    mainactivity에서
    .findFragmentById(R.id.map);의 map에 빨간색으로 오류가 뜨는데 이거 해결방법이 어떻게 될까요??

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

      레이아웃에 map이 있나보세요? 에러를 확인하여 구글에서 검색하면 원인을 찾을 수 있습니다

  21. Favicon of https://all-share-source-code.tistory.com BlogIcon 공유해드림 2020.11.30 13:12 신고

    와...정말 대단하십니다!!
    잘 작동되는거 확인 했고 이렇게 자세히 설명해주는 분은 없을꺼 같네요!!

    질문하나 있는데 혹시 구글Map API 빼고 위치정보만 받아오게 할수는 있나요?
    지금 제가 작업 하고 있긴한데 만만치 않네요...

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

      구글 Map API 없이라는게 지도에 표시안하고를 의미하나요?

+ Recent posts