ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Android Google Map에서 목적지 Marker와 이동 중인 현재 위치 간의 거리 계산해서 보여주기
    Android/Google Map 2019. 11. 25. 22:37




    Google Map에 목적지 마커를 추가한 후, 사용자가 해당 마커 위치로 이동할 때마다 남은 거리를 계산 해주도록  작성해보았습니다.

    2017. 11. 30    최초작성

    ~~~~~~~~~

    2019. 11. 25 FusedLocationProviderClient로 변경




    다음 포스팅에 있는  코드를 기반으로 작성되었습니다. 

    먼저 아래 코드로 진행해보고 나서 진행하세요..


    Android Google Map에 현재 위치 표시하기( FusedLocationProviderClient 사용)

    https://webnautes.tistory.com/1249 



    GenyMotion을 사용하여 Google Map 테스트를 진행했습니다.

    자세한 사용 방법은 다음 포스팅을 참고하세요.


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

    http://webnautes.tistory.com/1064




    현재 위치를 시청역으로 설정했습니다.





    목적지가 될 회현역에 마커를 추가하기 위해서 오랫동안 클릭하면 다음처럼 다이얼로그 창이 뜹니다.

    마커 클릭시 보여줄 정보를 입력한 후, 완료를 클릭합니다. 





    빨간색 마커가 추가되며 클릭해보면 좀전에 입력했던 정보를 보여줍니다.





    앱 하단의 버튼을 클릭하면 현재 위치와 목적지 마커 사이의  거리 계산을 시작합니다.





    현재 위치를 이동시키면 회현역까지 몇미터 남았는지 알려줍니다.

    (목적지와의 거리가 몇미터 이내이면 도착으로 할지 결정해서 이후 코딩을 진행하시면 될 듯합니다. )





    목적지와 현재 위치간의 거리 계산을 위해서 SphericalUtil의 computeDistanceBetween 메소드를 사용했습니다.


    double computeDistanceBetween(LatLng from, LatLng to)





    다음처럼 코드 수정을 진행합니다.



    1. SphericalUtil를 사용하기 위해서는 build.gradle에 다음 한 줄을 추가해야 합니다. 


    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'
        implementation 'com.google.maps.android:android-maps-utils:0.5'
        testImplementation 'junit:junit:4.12'
        androidTestImplementation 'androidx.test:runner:1.1.1'
        androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
    }





    2. 다음처럼 3개의 변수를 선언합니다. 


        Location mCurrentLocatiion;

        LatLng currentPosition;

        LatLng previousPosition = null;

        Marker addedMarker = null;

        int tracking = 0;



      

    3. locationCallback에서 이전 위치와 현재 위치가 다른 경우 (!previousPosition.equals(currentPosition))

    정해진 목적지까지의 거리인 500미터이내에 있고(distance < radius) 트래킹 설정 중이고 (tracking == 1)

    목적지 마커가 설정되어 있다면 (addedMarker != null)


    Toast.makeText 메소드를 사용하여 현재위치와 목적지 사이의 거리를 출력해줍니다. 

    거리 계산에는 SphericalUtil.computeDistanceBetween 메소드를 사용했습니다. 


      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);


                    previousPosition = currentPosition;

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

                    if (previousPosition == null) previousPosition = currentPosition;

                    if ( (addedMarker != null) && tracking == 1 ) {
                        double radius = 500; // 500m distance.

                        double distance = SphericalUtil.computeDistanceBetween(currentPosition, addedMarker.getPosition());

                        if ((distance < radius) && (!previousPosition.equals(currentPosition))) {

                            Toast.makeText(MainActivity.this, addedMarker.getTitle() + "까지" + (int) distance + "m 남음", Toast.LENGTH_LONG).show();
                        }
                    }

                    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;
                }


            }

        };




    4. 원하는 위치를 사용자가 길게 클릭했을 경우  마커를 추가하기 위해서 onMapReady 메서드에 다음 코드를 추가합니다.

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

            mMap = googleMap;


            mMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener(){

                @Override
                public void onMapLongClick(final LatLng latLng) {

                    AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                    LayoutInflater inflater = getLayoutInflater();
                    View view = inflater.inflate(R.layout.dialog_place_info, null);
                    builder.setView(view);
                    final Button button_submit = (Button) view.findViewById(R.id.button_dialog_placeInfo);
                    final EditText editText_placeTitle = (EditText) view.findViewById(R.id.editText_dialog_placeTitle);
                    final EditText editText_placeDesc = (EditText) view.findViewById(R.id.editText_dialog_placeDesc);

                    final AlertDialog dialog = builder.create();
                    button_submit.setOnClickListener(new View.OnClickListener() {
                        public void onClick(View v) {
                            String string_placeTitle = editText_placeTitle.getText().toString();
                            String string_placeDesc = editText_placeDesc.getText().toString();
                            Toast.makeText(MainActivity.this, string_placeTitle+"\n"+string_placeDesc,Toast.LENGTH_SHORT).show();


                            //맵을 클릭시 현재 위치에 마커 추가
                            MarkerOptions markerOptions = new MarkerOptions();
                            markerOptions.position(latLng);
                            markerOptions.title(string_placeTitle);
                            markerOptions.snippet(string_placeDesc);
                            markerOptions.draggable(true);
                            markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));

                            if ( addedMarker != null ) mMap.clear();
                            addedMarker = mMap.addMarker(markerOptions);

                            dialog.dismiss();
                        }
                    });

                    dialog.show();

                }
            });




    5. 위 코드에서 사용한 커스텀 다이얼로그를 위해 레이아웃 파일을 다음처럼 작성합니다.


    dialog_place_info.xml


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

        <TextView
            android:id="@+id/textView_dialog_placeTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:layout_alignParentStart="true"
            android:layout_alignParentTop="true"
            android:layout_marginLeft="22dp"
            android:layout_marginStart="22dp"
            android:layout_marginTop="26dp"
            android:text="장소 이름" />

        <EditText
            android:id="@+id/editText_dialog_placeTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentTop="true"
            android:layout_marginLeft="37dp"
            android:layout_marginStart="37dp"
            android:layout_marginTop="16dp"
            android:layout_toEndOf="@+id/textView_dialog_placeTitle"
            android:layout_toRightOf="@+id/textView_dialog_placeTitle"
            android:ems="10"
            android:inputType="textPersonName"
            android:text="" />

        <TextView
            android:id="@+id/textView_dialog_placeDesc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_below="@+id/editText_dialog_placeTitle"
            android:layout_marginTop="16dp"
            android:layout_toLeftOf="@+id/editText_dialog_placeTitle"
            android:layout_toStartOf="@+id/editText_dialog_placeTitle"
            android:text="장소 설명" />

        <EditText
            android:id="@+id/editText_dialog_placeDesc"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignLeft="@+id/editText_dialog_placeTitle"
            android:layout_alignStart="@+id/editText_dialog_placeTitle"
            android:layout_below="@+id/editText_dialog_placeTitle"
            android:ems="10"
            android:inputType="textPersonName"
            android:text="" />

        <Button
            android:id="@+id/button_dialog_placeInfo"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignEnd="@+id/editText_dialog_placeDesc"
            android:layout_alignRight="@+id/editText_dialog_placeDesc"
            android:layout_below="@+id/editText_dialog_placeDesc"
            android:layout_marginTop="26dp"
            android:text="완료" />

    </RelativeLayout>






    6. 사용자의 현재 위치와 목적지간의 거리 계산을 시작 또는 중지하기 위한 버튼을 지도 아래에 추가해줍니다.


    activity_main.xml


    <?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"
        android:orientation="vertical"
        tools:context=".MainActivity" >

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

        <Button
            android:id="@+id/button"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:text="Start" />

    </LinearLayout>





    7. MainActivity의 OnCreate 메소드에 다음 코드를 추가합니다.

    버튼 클릭시 변경되는 트레킹 여부를 저장하기 위해  int 타입의 tracking 변수를 이용했습니다.

    초기값은 0으로 해야합니다.

      

          final Button button = (Button)findViewById(R.id.button);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {

                    tracking = 1 - tracking;

                    if ( tracking == 1){
                        button.setText("Stop");
                    }
                    else button.setText("Start");
                }
            });



    이제 테스트를 해보면 됩니다. 



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

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

    유튜브 구 독 하 기
    후 원 하 기


    댓글 55

    • 도와주세욤 2017.12.02 12:21


      올려주신 현재위치 찾는 소스에다가 넣는거 맞죠??
      previousPosion랑 addedMarker 선언은 어디다가 해주는건가요,,, 에러가 떠가지구요

      그리고 한가지더 여쭤보고싶은게 있는데.. 마커 길게눌러서 찍는거 말고 제가 마커 지정 해서 거기까지 거리 알 수 있게 할 수 있나요??..

      수고많으십니다 답변좀 부탁드립니다!!!!

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.02 14:59 신고


        1. 포스팅을 약간 수정했습니다.
        2번 참고해서 4개의 변수를 선언하시면 되요..

        2. 마커 길게 눌렀을때 동작하는 onMapLongClick 메소드에 있는 내용 대신에 원하는 것으로 대체하시면 됩니다.

      • 도와주세욤 2017.12.02 18:03


        시청이 현재위치로 설정되었는데
        그거 설정소스는 뭔가요??
        좌표가 아무리 찾아도안보이는데..
        그거지우고싶어서요!

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


        아래 위치에 있습니다.

        GPS 수신이 아직 안된경우를 대비해서 서울로 지정해놓은 겁니다...

        안해놓으면 GPS 수신안되면 엉뚱하게 아프리카 대륙 근처 바다가 보입니다.


        public void setDefaultLocation() {

        mMoveMapByUser = false;


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

      • 도와주세욤 2017.12.02 18:47


        아 너무나 감사합니다...ㅜㅜㅜㅜ
        그런데 하나만 더 여쭤볼게요 ㅜㅜㅜ
        LongClick 이벤트로 한번까지는 괜찮은데
        두번 이상 LongClick 이벤트 하면 제가 추가해놓은 마커들이 다 사라지더라구요,, 혹시 이거 막을 방법이 있을까요,,,

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.02 19:06 신고



        아래 부분을 삭제하면 됩니다.

        if ( addedMarker != null ) mGoogleMap.clear();

      • 도와주세욤 2017.12.02 21:05


        죄송합니다.. 위에꺼는 말씀대로해서 다 됐는데
        마지막 7번 OnCreate 메소드에 추가를 했는데
        button.setOnClickListener(new View.OnClickListener() {
        부분 때문에 어플실행이 안되네요..
        어떻게해야할까요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.02 21:25 신고


        아래부분을 사용안하나요?

        final Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        tracking = 1 - tracking;

        if ( tracking == 1){
        button.setText("Stop");
        }
        else button.setText("Start");
        }
        });

      • 도와주세욤 2017.12.02 21:29


        네 사용했습니다 OnCreate 부분에 넣었는데 에러는 안나는데 Run 시키면 앱이 중지되네요

        로그는 이렇습니다
        Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
        at com.example.dm.distance.MainActivity.onCreate(MainActivity.java:102)
        at android.app.Activity.performCreate(Activity.java:6912)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1126)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2870)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2978) 
        at android.app.ActivityThread.-wrap14(ActivityThread.java) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1628) 
        at android.os.Handler.dispatchMessage(Handler.java:102) 
        at android.os.Looper.loop(Looper.java:154) 
        at android.app.ActivityThread.main(ActivityThread.java:6646) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1468) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1358) 

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.02 21:44 신고


        일단 해당부분을 주석처리해놓고 실행되는지 보세요

      • 도와주세욤 2017.12.02 21:52


        해당부분에 오류가 너무많아서 주석처리로는 안될것같습니다.


        final Button button = (Button)findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {

        tracking = 1 - tracking;

        if ( tracking == 1){
        button.setText("Stop");
        }
        else button.setText("Start");
        }
        });

        이소스에 문제가 있는것 같은데

        protected void onCreate(Bundle savedInstanceState){
        이 메소드 안에 넣는것이 맞나요???..
        죄송합니다 정말

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.02 23:28 신고


        언급하신 부분 없이는 실행이 되나요?
        아래 용도로 사용되는 건데 필요없으면 지우셔도 됩니다.

        해당 위치가 맞습니다...

        버튼눌렀을때 버튼에 보여지는 문자열을 바꿔주는 거랑 tracking 변수값을 바꿔주는 역할만 합니다..

        한번 누르면 STOP으로 바꾸고 tracking은 1로 바꾸고
        다시 누르면 START로 바꾸고 tracking은 0으로 바꿉니다.


        tracking 변수가 1일때에만 사용자의 위치와 해당 위치간에 값을 비교하게 됩니다.

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.02 23:30 신고


        참.. 아래 줄 다음에 넣으셔야 합니다..

        setContentView(R.layout.activity_main);

      • 도와주세욤 2017.12.03 12:31


        아 너무 죄송합니다 잘되네요 ㅠㅠㅠ
        제가 밑에 소스를 잘못입력했습니다
        소스에 문제없습니다!
        너무너무감사합니다!!! 많이 공부하고갑니다!!!!

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.03 12:33 신고


        oncreate메소드 전체를 올려보세요

    • ㅜㅜㅜㅜ 2017.12.02 12:43


      혹시 전체 자바소스는 없나요 ㅜㅜㅜ
      헷갈리네요

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2017.12.02 15:01 신고


        GoogleApiClient와 FusedLocationApi를 사용하여 Android Google Map에 현재 위치 표시하기
        http://webnautes.tistory.com/1011


        다음 포스팅의 코드를 기반으로 추가하시면 됩니다..

        추가하는 항목들에 번호들을 다시 부여해놓았으니 안되는 부분을 알려주세요

    • 감사합니다. 2018.06.07 01:22


      안녕하세요 잘 배우고 있습니다!
      혹시 이 포스팅과 다른 포스팅을 합쳐서 주변 음식점을 검색하면 나오게 한 후 하나의 마커를 클릭하고 나서 START 버튼을 눌렀을때 그 음식점까지의 거리가 얼마나 되는지 알 수 있는 방법이 있을까요?

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2018.06.07 10:05 신고


        다음 함수의 addedMarker 대신에 클릭한 마커로 바꾸면 될듯합니다.

        double distance = SphericalUtil.computeDistanceBetween(currentPosition, addedMarker.getPosition());

      • 감사합니다. 2018.06.08 00:45


        댓글 감사합니다! 참고하여 바꾸어 보려고 했으나 잘 모르겠어서 다시한번 질문드립니다! 혹시 아래와 같은 방식으로 구글 플레이스를 통해 1000미터 내에 버스정류장을 검색하여 마커를 생성하는 방식인데 어떠한 방법으로 addedMarker대신에 넣어야 할지 알려주실수 있을까요..?? ㅜㅜ


        new NRPlaces.Builder()
        .listener(MainActivity.this)
        .key("구글플레이스AP키")
        .latlng(location.latitude, location.longitude)//현재 위치
        .radius(1000) //1000 미터 내에서 검색
        .type(PlaceType.BUS_STATION) //버스정류장
        .build()
        .execute();

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2018.06.08 14:57 신고


        해당 메소드를 호출하면

        결과는 다음 메소드에서 확인가능합니다.

        @Override
        public void onPlacesSuccess(final List<Place> places) {


        다음처럼 호출한 장소들에 대한 위도, 경도 정보를 가져오게 되는데..

        for (noman.googleplaces.Place place : places) {


        LatLng latLng

        = new LatLng(place.getLatitude()

        , place.getLongitude());


        여기서 얻은 값을 위에 언급한 함수에 넣어주시면 됩니다.



        제 블로그의 다음 포스팅에서 onPlacesSuccess 관련 코드 부분을 참고하세요.

        Places API Web Service를 사용하여 Android Google Map에 현재 위치 주변의 음식점 표시하기
        http://webnautes.tistory.com/1080

      • 질문입니다 2019.11.26 02:31


        안녕하세요 위에 분이 썻던 부분 코드로 구현해보고있는데 질문이 있습니다.
        public void onPlacesSuccess(final List<Place> places) 에 있는 Latlng에 대한 값을 가져오려면 다른 메소드에 있는 변수기 때문에 전역변수로 Latlng를 선언하고 불러와야 하지않나요??

        그래서 전역 변수 List로 선언한 Latlng값들 중에서 클릭한 마커를 불러와야하는데 마커가 여러개다 보니 몇 번째있는 List값을 가져와야할지 모르겠습니다..

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.11.26 09:26 신고


        위치 정보를 성공적으로 가져올 때 마다 다음 코드에서 places에 있는 장소 리스트를 전역변수로 넘겨주세요.

        public void onPlacesSuccess(final List<Place> places)

    • 동동이 2018.11.11 23:42


      안녕하세요 작성하신 글이 도움이 많이 되고있습니다.

      실례하지만 질문하나만 하겠습니다

      두 마커 사이의 거리가 길에 따른 거리인가요 아니면 직선거리인가요?

    • 학생 2019.04.07 16:29


      현재위치에서 주변음식점 찾는소스에서 넣어보고있는데요..
      start버튼을 누르니 아무것도 실행이안되고 위치정보를 가져올수 없다고 마커에 표시가되는데
      현재위치에서 주변 음식점까지 거리를 구하게 하고싶습니다

      답변 부탁드립니다!!

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.04.07 17:04 신고


        로그캣에서 에러 메시지를 찾아야 해결이 가능합니다..

        로그캣을 켜놓고 start버튼을 클릭하면 에러 메시지가 보일겁니다.

    • 학생 2019.04.08 01:22


      어느곳이 에러인지 잘못찾겠습니다..
      제공해주신 현재위치에서 음식점찾는것에서 거리계산하는 코드를 넣어보았습니다
      잘못된점 지적해주시면 감사하겠습니다
      mainactivity 소스입니다

      public class MainActivity extends AppCompatActivity
      implements OnMapReadyCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener, PlacesListener {

      private GoogleApiClient mGoogleApiClient = null;
      private GoogleMap mGoogleMap = null;
      private Marker currentMarker = null;

      private static final String TAG = "googlemap_example";
      private static final int GPS_ENABLE_REQUEST_CODE = 2001;
      private static final int PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION = 2002;
      private static final int UPDATE_INTERVAL_MS = 1000;
      private static final int FASTEST_UPDATE_INTERVAL_MS = 500;

      private AppCompatActivity mActivity;
      boolean askPermissionOnceAgain = false;
      boolean mRequestingLocationUpdates = false;
      Location mCurrentLocation;
      boolean mMoveMapByUser = true;
      boolean mMoveMapByAPI = true;

      LatLng currentPosition = null;
      LatLng previousPosition = null;
      Marker addedMarker = null;
      int tracking = 0;

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

      List<Marker> previous_marker =null;
      @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);


      getWindow().setFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON,
      WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
      setContentView(R.layout.activity_main);

      Log.d(TAG,"onCreate");

      previous_marker = new ArrayList<Marker>();

      final Button button1 = (Button)findViewById(R.id.button1);
      Button button = (Button)findViewById(R.id.button);
      button.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
      showPlaceInformation(currentPosition);

      tracking = 1 - tracking;

      if ( tracking == 1){
      button1.setText("Stop");
      }
      else button1.setText("Start");
      }
      });


      mActivity=this;

      mGoogleApiClient = new GoogleApiClient.Builder(this)
      .addConnectionCallbacks(this)
      .addOnConnectionFailedListener(this)
      .addApi(LocationServices.API)
      .build();

      MapFragment mapFragment = (MapFragment)getFragmentManager()
      .findFragmentById(R.id.map);
      mapFragment.getMapAsync(this);
      }
      @Override
      public void onResume() {

      super.onResume();

      if (mGoogleApiClient.isConnected()) {

      Log.d(TAG, "onResume : call startLocationUpdates");
      if (!mRequestingLocationUpdates) startLocationUpdates();
      }
      if (askPermissionOnceAgain) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
      askPermissionOnceAgain = false;

      checkPermissions();
      }
      }
      }

      private void startLocationUpdates() {

      if (!checkLocationServicesStatus()) {

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

      if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
      && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

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


      Log.d(TAG, "startLocationUpdates : call FusedLocationApi.requestLocationUpdates");
      LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
      mRequestingLocationUpdates = true;

      mGoogleMap.setMyLocationEnabled(true);

      }

      }

      private void stopLocationUpdates() {

      Log.d(TAG,"stopLocationUpdates : LocationServices.FusedLocationApi.removeLocationUpdates");
      LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
      mRequestingLocationUpdates = false;
      }

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

      mGoogleMap.getUiSettings().setMyLocationButtonEnabled(true);
      mGoogleMap.animateCamera(CameraUpdateFactory.zoomTo(15));
      mGoogleMap.setOnMyLocationButtonClickListener(new GoogleMap.OnMyLocationButtonClickListener() {

      @Override
      public boolean onMyLocationButtonClick() {

      Log.d(TAG, "onMyLocationButtonClick : 위치에 따른 카메라 이동 활성화");
      mMoveMapByAPI = true;
      return true;
      }
      });

      mGoogleMap.setOnInfoWindowClickListener(new GoogleMap.OnInfoWindowClickListener() {

      @Override
      public void onInfoWindowClick(Marker marker) {

      Intent intent = new Intent(getBaseContext(), NewActivity.class);

      String title = marker.getTitle();
      String address = marker.getSnippet();

      intent.putExtra("title", title);
      intent.putExtra("address", address);

      startActivity(intent);
      }
      });

      mGoogleMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
      @Override
      public void onMapClick(LatLng latLng) {
      Log.d(TAG, "onMapClick");
      }
      });
      mGoogleMap.setOnCameraMoveStartedListener(new GoogleMap.OnCameraMoveStartedListener() {

      @Override
      public void onCameraMoveStarted(int i) {

      if (mMoveMapByUser == true && mRequestingLocationUpdates) {

      Log.d(TAG, "onCameraMove : 위치에 따른 카메라 이동 비활성화");
      mMoveMapByAPI = false;
      }

      mMoveMapByUser = true;

      }
      });

      mGoogleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {

      @Override
      public void onCameraMove() {


      }
      });
      mGoogleMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener(){

      @Override
      public void onMapLongClick(final LatLng latLng) {

      AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
      LayoutInflater inflater = getLayoutInflater();
      View view = inflater.inflate(R.layout.dialog_place_info, null);
      builder.setView(view);
      final Button button_submit = (Button) view.findViewById(R.id.button_dialog_placeInfo);
      final EditText editText_placeTitle = (EditText) view.findViewById(R.id.editText_dialog_placeTitle);
      final EditText editText_placeDesc = (EditText) view.findViewById(R.id.editText_dialog_placeDesc);

      final AlertDialog dialog = builder.create();
      button_submit.setOnClickListener(new View.OnClickListener() {
      public void onClick(View v) {
      String string_placeTitle = editText_placeTitle.getText().toString();
      String string_placeDesc = editText_placeDesc.getText().toString();
      Toast.makeText(MainActivity.this, string_placeTitle+"\n"+string_placeDesc,Toast.LENGTH_SHORT).show();


      //맵을 클릭시 현재 위치에 마커 추가
      MarkerOptions markerOptions = new MarkerOptions();
      markerOptions.position(latLng);
      markerOptions.title(string_placeTitle);
      markerOptions.snippet(string_placeDesc);
      markerOptions.draggable(true);
      markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));

      if ( addedMarker != null ) mGoogleMap.clear();
      addedMarker = mGoogleMap.addMarker(markerOptions);

      dialog.dismiss();
      }
      });

      dialog.show();

      }
      });
      }



      @Override
      public void onLocationChanged(Location location) {

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

      if (previousPosition == null) previousPosition = currentPosition;


      if ((addedMarker != null) && tracking == 1) {
      double radius = 500; // 500m distance.

      double distance = SphericalUtil.computeDistanceBetween(currentPosition, addedMarker.getPosition());

      if ((distance < radius) && (!previousPosition.equals(currentPosition))) {

      Toast.makeText(this, addedMarker.getTitle() + "까지" + (int) distance + "m 남음", Toast.LENGTH_LONG).show();


      Log.d(TAG, "onLocationChanged : ");

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

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

      mCurrentLocation = location;
      }
      }
      }

      @Override
      protected void onStart() {

      if(mGoogleApiClient != null && mGoogleApiClient.isConnected() == false){

      Log.d(TAG, "onStart: mGoogleApiClient connect");
      mGoogleApiClient.connect();
      }

      super.onStart();
      }

      @Override
      protected void onStop() {

      if (mRequestingLocationUpdates) {

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

      if ( mGoogleApiClient.isConnected()) {

      Log.d(TAG, "onStop : mGoogleApiClient disconnect");
      mGoogleApiClient.disconnect();
      }

      super.onStop();
      }

      @Override
      public void onConnected(Bundle connectionHint) {


      if ( mRequestingLocationUpdates == false ) {

      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

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

      if (hasFineLocationPermission == PackageManager.PERMISSION_DENIED) {

      ActivityCompat.requestPermissions(mActivity,
      new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
      PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);

      } else {

      Log.d(TAG, "onConnected : 퍼미션 가지고 있음");
      Log.d(TAG, "onConnected : call startLocationUpdates");
      startLocationUpdates();
      mGoogleMap.setMyLocationEnabled(true);
      }

      }else{

      Log.d(TAG, "onConnected : call startLocationUpdates");
      startLocationUpdates();
      mGoogleMap.setMyLocationEnabled(true);
      }
      }
      }

      @Override
      public void onConnectionFailed(ConnectionResult connectionResult) {

      Log.d(TAG, "onConnectionFailed");
      setDefaultLocation();
      }


      @Override
      public void onConnectionSuspended(int cause) {

      Log.d(TAG, "onConnectionSuspended");
      if (cause == CAUSE_NETWORK_LOST)
      Log.e(TAG, "onConnectionSuspended(): Google Play services " +
      "connection lost. Cause: network lost.");
      else if (cause == CAUSE_SERVICE_DISCONNECTED)
      Log.e(TAG, "onConnectionSuspended(): Google Play services " +
      "connection lost. Cause: service disconnected");
      }

      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);
      }

      }


      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) {

      mMoveMapByUser = false;


      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 = mGoogleMap.addMarker(markerOptions);


      if ( mMoveMapByAPI ) {

      Log.d( TAG, "setCurrentLocation : mGoogleMap moveCamera "
      + location.getLatitude() + " " + location.getLongitude() ) ;
      // CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLngZoom(currentLatLng, 15);
      CameraUpdate cameraUpdate = CameraUpdateFactory.newLatLng(currentLatLng);
      mGoogleMap.moveCamera(cameraUpdate);
      }
      }


      public void setDefaultLocation() {

      mMoveMapByUser = false;


      //디폴트 위치, 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 = mGoogleMap.addMarker(markerOptions);

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

      }


      //여기부터는 런타임 퍼미션 처리을 위한 메소드들
      @TargetApi(Build.VERSION_CODES.M)
      private void checkPermissions() {
      boolean fineLocationRationale = ActivityCompat
      .shouldShowRequestPermissionRationale(this,
      Manifest.permission.ACCESS_FINE_LOCATION);
      int hasFineLocationPermission = ContextCompat.checkSelfPermission(this,
      Manifest.permission.ACCESS_FINE_LOCATION);

      if (hasFineLocationPermission == PackageManager
      .PERMISSION_DENIED && fineLocationRationale)
      showDialogForPermission("앱을 실행하려면 퍼미션을 허가하셔야합니다.");

      else if (hasFineLocationPermission
      == PackageManager.PERMISSION_DENIED && !fineLocationRationale) {
      showDialogForPermissionSetting("퍼미션 거부 + Don't ask again(다시 묻지 않음) " +
      "체크 박스를 설정한 경우로 설정에서 퍼미션 허가해야합니다.");
      } else if (hasFineLocationPermission == PackageManager.PERMISSION_GRANTED) {


      Log.d(TAG, "checkPermissions : 퍼미션 가지고 있음");

      if ( mGoogleApiClient.isConnected() == false) {

      Log.d(TAG, "checkPermissions : 퍼미션 가지고 있음");
      mGoogleApiClient.connect();
      }
      }
      }

      @Override
      public void onRequestPermissionsResult(int permsRequestCode,
      @NonNull String[] permissions,
      @NonNull int[] grantResults) {

      if (permsRequestCode
      == PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION && grantResults.length > 0) {

      boolean permissionAccepted = grantResults[0] == PackageManager.PERMISSION_GRANTED;

      if (permissionAccepted) {


      if ( mGoogleApiClient.isConnected() == false) {

      Log.d(TAG, "onRequestPermissionsResult : mGoogleApiClient connect");
      mGoogleApiClient.connect();
      }

      } else {

      checkPermissions();
      }
      }
      }

      @TargetApi(Build.VERSION_CODES.M)
      private void showDialogForPermission(String msg) {

      AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
      builder.setTitle("알림");
      builder.setMessage(msg);
      builder.setCancelable(false);
      builder.setPositiveButton("예", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
      ActivityCompat.requestPermissions(mActivity,
      new String[]{android.Manifest.permission.ACCESS_FINE_LOCATION},
      PERMISSIONS_REQUEST_ACCESS_FINE_LOCATION);
      }
      });

      builder.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
      finish();
      }
      });
      builder.create().show();
      }

      private void showDialogForPermissionSetting(String msg) {

      AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
      builder.setTitle("알림");
      builder.setMessage(msg);
      builder.setCancelable(true);
      builder.setPositiveButton("예", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {

      askPermissionOnceAgain = true;

      Intent myAppSettings = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
      Uri.parse("package:" + mActivity.getPackageName()));
      myAppSettings.addCategory(Intent.CATEGORY_DEFAULT);
      myAppSettings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
      mActivity.startActivity(myAppSettings);
      }
      });
      builder.setNegativeButton("아니오", new DialogInterface.OnClickListener() {
      public void onClick(DialogInterface dialog, int id) {
      finish();
      }
      });
      builder.create().show();
      }

      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
      public void onPlacesFailure(PlacesException e) {

      }

      @Override
      public void onPlacesStart() {

      }

      @Override
      public void onPlacesSuccess(final List<Place> places) {

      runOnUiThread(new Runnable() {

      @Override

      public void run() {

      for (noman.googleplaces.Place place : places) {


      LatLng latLng = new LatLng(place.getLatitude(), place.getLongitude());

      String markerSnippet = getCurrentAddress(latLng);

      MarkerOptions markerOptions = new MarkerOptions();
      markerOptions.position(latLng);
      markerOptions.title(place.getName());
      markerOptions.snippet(markerSnippet);
      Marker item = mGoogleMap.addMarker(markerOptions);
      previous_marker.add(item);

      }

      //중복 마커 제거

      HashSet<Marker> hashSet = new HashSet<Marker>();
      hashSet.addAll(previous_marker);
      previous_marker.clear();
      previous_marker.addAll(hashSet);
      }
      });
      }

      public void showPlaceInformation(LatLng location){
      mGoogleMap.clear();

      if(previous_marker!=null)
      previous_marker.clear();

      new NRPlaces.Builder().listener(MainActivity.this).key("api키").latlng(location.latitude, location.longitude).radius(1000).type(PlaceType.PARKING).build().execute();
      }
      @Override
      public void onPlacesFinished() {

      }
      @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 : 퍼미션 가지고 있음");


      if ( mGoogleApiClient.isConnected() == false ) {

      Log.d( TAG, "onActivityResult : mGoogleApiClient connect ");
      mGoogleApiClient.connect();
      }
      return;
      }
      }
      break;
      }
      }
      }

    • 학생 2019.04.08 12:29


      구글맵을 눌러서 마커를 생성까지는했는데 스타트버튼을눌러도 실행이안됩니다..

      2019-04-08 12:28:10.707 3157-3157/? E/Zygote: isWhitelistProcess - Process is Whitelisted
      2019-04-08 12:28:10.708 3157-3157/? E/Zygote: accessInfo : 1
      2019-04-08 12:28:14.486 3157-3157/com.example.myapplication E/SchedPolicy: set_timerslack_ns write failed: Operation not permitted
      2019-04-08 12:28:18.898 3157-3157/com.example.myapplication E/SchedPolicy: set_timerslack_ns write failed: Operation not permitted
      2019-04-08 12:28:18.910 3157-3157/com.example.myapplication E/ViewRootImpl: sendUserActionEvent() returned.
      2019-04-08 12:28:22.770 3157-3157/com.example.myapplication E/SchedPolicy: set_timerslack_ns write failed: Operation not permitted
      2019-04-08 12:28:23.147 3157-3206/com.example.myapplication E/BufferQueueProducer: [SurfaceTexture-0-3157-0] disconnect: not connected (req=1)

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.04.08 19:06 신고


        문제와 관련된 에러 메시지가 없습니다.

        스타트 버튼을 클릭 후 출력되는 로그켓의 메시지를 다시 확인해보세요.

    • 학생 2019.04.09 00:12


      스타트버튼을누르면 아무것도실행을안합니다 에러또한 안뜹니다
      이경우 자바코드가 잘못된것인가요??
      그리고 위에 댓글을보고 addedmarker 대신 클릭하는 마커로 바꾸라하셧는데 addedmarker부분을 클릭한 마커로 바꾸는것을 어떻게 바꿔야하는지 못찾겠습니다...

    • 학생 2019.04.09 01:47


      아아 해결했습니다 답변정말 감사합니다
      마지막으로 위에 addedmarker을 클릭 마커로 바꾸는방법을 알고싶습니다!

    • Favicon of http://www.cyworld.com/01026664196 BlogIcon 도와주세요ㅠ 2019.05.15 21:41


      Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.Button.setOnClickListener(android.view.View$OnClickListener)' on a null object reference
      at com.example.user.gps.MainActivity.onCreate(MainActivity.java:124)
      이것이 오류로 뜨는데 어떡해야되죠ㅠㅠ
      Button 객체가 null 인 상태에서 click리스너를 등록해서 생긴 문제라는데 찾기가 힘드네요

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.05.15 22:01 신고


        findviewbyid에 적은 버튼 리소스 이름이 틀리지 않았는지 확인해보세요.

      • 도와주세요ㅠ 2019.05.16 12:12


        button이라는 아이디로 만들었고 그거를 리스너에 아이디 그대로 입력했는데 왜그런걸까요 ㅠ 블로그 내용대로 썼는데..

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.05.16 12:28 신고


        레이아웃 파일과 MainActivity에서 버튼 가져오는 부분을 비교해보세요..

        다른 부분때문에 에러일 것 같지는 않습니다.

    • ㅠㅠ 2019.10.07 23:39


      어플이 실행이 안되고 중지 되여 다른 에러 메시지 안뜨는데 어디가 문제일까요?
      로그켓은 이렇게 나와요
      10-07 23:38:02.438 3516-3516/com.example.map E/AndroidRuntime: FATAL EXCEPTION: main
      Process: com.example.map, PID: 3516
      java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.map/com.example.map.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.gms.maps.MapFragment.getMapAsync(com.google.android.gms.maps.OnMapReadyCallback)' on a null object reference
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2327)
      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2392)
      at android.app.ActivityThread.access$800(ActivityThread.java:153)
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1305)
      at android.os.Handler.dispatchMessage(Handler.java:102)
      at android.os.Looper.loop(Looper.java:135)
      at android.app.ActivityThread.main(ActivityThread.java:5293)
      at java.lang.reflect.Method.invoke(Native Method)
      at java.lang.reflect.Method.invoke(Method.java:372)
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
      Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.gms.maps.MapFragment.getMapAsync(com.google.android.gms.maps.OnMapReadyCallback)' on a null object reference
      at com.example.map.MainActivity.onCreate(MainActivity.java:111)
      at android.app.Activity.performCreate(Activity.java:5990)
      at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
      at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2280)
      at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2392) 
      at android.app.ActivityThread.access$800(ActivityThread.java:153) 
      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1305) 
      at android.os.Handler.dispatchMessage(Handler.java:102) 
      at android.os.Looper.loop(Looper.java:135) 
      at android.app.ActivityThread.main(ActivityThread.java:5293) 
      at java.lang.reflect.Method.invoke(Native Method) 
      at java.lang.reflect.Method.invoke(Method.java:372) 
      at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903) 
      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698) 

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.08 08:50 신고


        자바코드와 레이아웃에서 MapFragment 대신에 SupportMapFragment를 사용해야 합니다.
        아래 링크를 참고하세요

        https://webnautes.tistory.com/647

    • Thankswebnautes 2019.10.13 16:56


      글보고 많은 도움 받았습니다!
      저도 지도를 만들려고 하는데 오픈 API를 파싱해서 위치를 나오게 하고싶은데 파싱을 하는법을 모르겠네요..ㅠㅠ
      파싱방법도 글 써주실수는 없을까요...?도움받고싶습니다...

    • Thankswebnautes 2019.10.14 16:20


      xml입니다!!

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.10.14 16:25 신고


        아직 블로그에 XML 파싱관련 글은 없습니다.

        구글에서 android xml parsing라고 검색하시면 원하는 내용을 찾을 수 있습니다.

    • 2019.11.25 03:37


      비밀댓글입니다

    • 2019.12.06 03:09


      비밀댓글입니다

      • Favicon of https://webnautes.tistory.com BlogIcon webnautes 2019.12.06 09:34 신고


        다른 액티비티로 넘어가기전 SharedPreferences 를 사용하여 현재 값들을 저장하고 다시 돌아올때 다시 SharedPreferences를 사용하여 저장된 값을 불러와야 할 듯합니다.

      • 2019.12.06 20:05


        비밀댓글입니다

    • 정정욱 2019.12.09 15:41


      안녕하세요
      올린 글 중에서..주번 맛집 찾는 글을 봤었는데
      혹시 맛집을 찾고나서 현제 나의 위치에서 선택한 맛집의 거리를 재볼려 하는데 어떻게 접근하면 좋을까요..

    • 정정욱 2019.12.10 14:31


      previousPosition = currentPosition;

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

      if (previousPosition == null) previousPosition = currentPosition;

      if ( (addedMarker != null) && tracking == 1 ) {
      double radius = 500; // 500m distance.

      double distance = SphericalUtil.computeDistanceBetween(currentPosition, addedMarker.getPosition());

      if ((distance < radius) && (!previousPosition.equals(currentPosition))) {

      Toast.makeText(MainActivity.this, addedMarker.getTitle() + "까지" + (int) distance + "m 남음", Toast.LENGTH_LONG).show();
      }
      }
      혹시 이 코드만 쓰면 나오는 걸까요?

Designed by Tistory.