관리 메뉴

웹개발자의 기지개

[안드로이드] 구글맵으로 GPS 현재위치 실시간 연동하기 본문

안드로이드

[안드로이드] 구글맵으로 GPS 현재위치 실시간 연동하기

http://portfolio.wonpaper.net 2020. 4. 18. 01:09

[ 실행화면 설명 ]

1. 최근위치1 텍스트로 가장 최근에 GPS 지점을 화면에 표시해준다. 

: 마커 이미지 화면표시, 1Km 반경 원표시 기능

2. 인공위성 GPS 연동될 경우에 매초마다 현재 위치가 자동 화면 중앙에 표시된다.

: 마커와 반경이 자동이동된다.

 

 

[깃허브 소스 공개]

https://github.com/wonpaper/android-GPS-MyLocation

 

wonpaper/android-GPS-MyLocation

Get current location information through GPS. Contribute to wonpaper/android-GPS-MyLocation development by creating an account on GitHub.

github.com

 

 

[ activity_main.xml ] - xml 디자인구성

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">
 
    <Button
        android:id="@+id/button1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" android:text="현재위치 확인하기"/>
 
    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
 
    <Button
        android:id="@+id/button2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" android:text="구글지도맵 내위치"/>
 
    <fragment
        android:id="@+id/map"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="com.google.android.gms.maps.SupportMapFragment"/>
 
</LinearLayout>
cs

위의 xml 디자인페이지의 핵심은 button2를 클릭할 경우 fragment 형태로 현재 GPS 위치가 실시간으로 화면에 보여준다.

 

[ build.gradle ( Module: app ) ] 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
allprojects {
    repositories {
        maven { url 'https://jitpack.io' }
    }
}
 
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation 'com.github.pedroSG94:AutoPermissions:1.0.3'
    implementation 'com.google.android.gms:play-services-maps:17.0.0'
}
cs

14라인은 AutoPermissions 라이브러리를 이용하여 권한기능을 자동화하기 위해 추가했다.

15라인은 구글 GPS 연동을 위해 꼭 추가했다.

 

또한 구글맵 연동을 위하여 구글 콘솔 페이지에서 구글 API키 발급받도록 하자.

https://console.developers.google.com/

 

Google Cloud Platform

하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하세요.

accounts.google.com

 

[ AndroidManifest.xml ] - 안드로이드 환경설정부분

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="wonpa.alwaysweb.com.myloctest">
    <permission android:name="wonpa.alwaysweb.com.myloctest.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />
 
    <uses-permission android:name="wonpa.alwaysweb.com.myloctest.permission.MAPS_RECEIVE" />
 
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
 
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
 
    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true"/>
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <uses-library android:name="com.google.android.maps" />
        <uses-library
            android:name="org.apache.http.legacy"
            android:required="false" />
 
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="구글계정api키값" />
 
        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
 
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
 
 
    </application>
 
</manifest>
cs

위의 manifest 소스에서 12라인, 13라인이 GPS 연동을 위해 꼭 추가해야 하는 부분이다.

[ 인공위성 GPS 연동 ] - 현재위치가 정확하나, 실내에서는 회신이 잘 되지 않는 단점이 있다.

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

[ 기지국을 통한 GPS 연동 ]

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

 

[ MainActivity.java ]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
package wonpa.alwaysweb.com.myloctest;
 
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
 
import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
 
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapsInitializer;
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.Circle;
import com.google.android.gms.maps.model.CircleOptions;
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.pedro.library.AutoPermissions;
import com.pedro.library.AutoPermissionsListener;
 
public class MainActivity extends AppCompatActivity implements AutoPermissionsListener {
    private Button button1, button2;
    private TextView textView1;
    LocationManager manager;
    GPSListener gpsListener;
 
    SupportMapFragment mapFragment;
    GoogleMap map;
 
    Marker myMarker;
    MarkerOptions myLocationMarker;
    Circle circle;
    CircleOptions circle1KM;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        setTitle("GPS 현재위치 확인하기");
 
        button1 = findViewById(R.id.button1);
        button2 = findViewById(R.id.button2);
        textView1 = findViewById(R.id.textView1);
 
        manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        gpsListener = new GPSListener();
 
        try {
            MapsInitializer.initialize(this);
        } catch (Exception e) {
            e.printStackTrace();
        }
 
        mapFragment = (SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map);
        mapFragment.getMapAsync(new OnMapReadyCallback() {
            @Override
            public void onMapReady(GoogleMap googleMap) {
                Log.i("MyLocTest","지도 준비됨");
                map = googleMap;
                map.setMyLocationEnabled(true);
            }
        });
 
        // 최초 지도 숨김
        mapFragment.getView().setVisibility(View.GONE);
 
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
 
                // 지도 보임
                mapFragment.getView().setVisibility(View.VISIBLE);
                startLocationService();
            }
        });
 
        AutoPermissions.Companion.loadAllPermissions(this101);
    }
 
    public void startLocationService() {
        try {
            Location location = null;
 
            long minTime = 0;        // 0초마다 갱신 - 바로바로갱신
            float minDistance = 0;
 
            if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                location = manager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
 
                if (location != null) {
                    double latitude = location.getLatitude();
                    double longitude = location.getLongitude();
                    String message = "최근 위치1 -> Latitude : " + latitude + "\n Longitude : " + longitude;
 
                    textView1.setText(message);
                    showCurrentLocation(latitude, longitude);
                    Log.i("MyLocTest""최근 위치1 호출");
                }
 
                //위치 요청하기
                manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime, minDistance, gpsListener);
                //manager.removeUpdates(gpsListener);
                Toast.makeText(getApplicationContext(), "내 위치1확인 요청함", Toast.LENGTH_SHORT).show();
                Log.i("MyLocTest""requestLocationUpdates() 내 위치1에서 호출시작 ~~ ");
 
            } else if (manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
 
                location = manager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                if (location != null) {
                    double latitude = location.getLatitude();
                    double longitude = location.getLongitude();
                    String message = "최근 위치2 -> Latitude : " + latitude + "\n Longitude : " + longitude;
 
                    textView1.setText(message);
                    showCurrentLocation(latitude,longitude);
 
                    Log.i("MyLocTest","최근 위치2 호출");
                }
 
 
                //위치 요청하기
                manager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, minTime, minDistance, gpsListener);
                //manager.removeUpdates(gpsListener);
                Toast.makeText(getApplicationContext(), "내 위치2확인 요청함", Toast.LENGTH_SHORT).show();
                Log.i("MyLocTest","requestLocationUpdates() 내 위치2에서 호출시작 ~~ ");
            }
 
        } catch (SecurityException e) {
            e.printStackTrace();
        }
    }
 
 
    class GPSListener implements LocationListener {
 
        // 위치 확인되었을때 자동으로 호출됨 (일정시간 and 일정거리)
        @Override
        public void onLocationChanged(Location location) {
            double latitude = location.getLatitude();
            double longitude = location.getLongitude();
            String message = "내 위치는 Latitude : " + latitude + "\nLongtitude : " + longitude;
            textView1.setText(message);
 
            showCurrentLocation(latitude,longitude);
            Log.i("MyLocTest","onLocationChanged() 호출되었습니다.");
        }
 
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
 
        }
 
        @Override
        public void onProviderEnabled(String provider) {
 
        }
 
        @Override
        public void onProviderDisabled(String provider) {
 
        }
    }
 
    @Override
    protected void onResume() {
        super.onResume();
 
        // GPS provider를 이용전에 퍼미션 체크
        if (checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            // TODO: Consider calling
            //    Activity#requestPermissions
            // here to request the missing permissions, and then overriding
            //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
            //                                          int[] grantResults)
            // to handle the case where the user grants the permission. See the documentation
            // for Activity#requestPermissions for more details.
            Toast.makeText(getApplicationContext(),"접근 권한이 없습니다.",Toast.LENGTH_SHORT).show();
            return;
        } else {
 
            if (manager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                manager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 00, gpsListener);
                //manager.removeUpdates(gpsListener);
            } else if (manager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                manager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 00, gpsListener);
                //manager.removeUpdates(gpsListener);
            }
 
            if (map != null) {
                map.setMyLocationEnabled(true);
            }
            Log.i("MyLocTest","onResume에서 requestLocationUpdates() 되었습니다.");
        }
    }
 
    @Override
    protected void onPause() {
        super.onPause();
        manager.removeUpdates(gpsListener);
 
        if (map != null) {
            map.setMyLocationEnabled(false);
        }
        Log.i("MyLocTest","onPause에서 removeUpdates() 되었습니다.");
    }
 
    private void showCurrentLocation(double latitude, double longitude) {
        LatLng curPoint = new LatLng(latitude, longitude);
        map.animateCamera(CameraUpdateFactory.newLatLngZoom(curPoint, 15));
        showMyLocationMarker(curPoint);
    }
 
    private void showMyLocationMarker(LatLng curPoint) {
        if (myLocationMarker == null) {
            myLocationMarker = new MarkerOptions(); // 마커 객체 생성
            myLocationMarker.position(curPoint);
            myLocationMarker.title("최근위치 \n");
            myLocationMarker.snippet("*GPS로 확인한 최근위치");
            myLocationMarker.icon(BitmapDescriptorFactory.fromResource((R.drawable.mylocation)));
            myMarker = map.addMarker(myLocationMarker);
        } else {
            myMarker.remove(); // 마커삭제
            myLocationMarker.position(curPoint);
            myMarker = map.addMarker(myLocationMarker);
        }
 
        // 반경추가
        if (circle1KM == null) {
            circle1KM = new CircleOptions().center(curPoint) // 원점
                    .radius(1000)       // 반지름 단위 : m
                    .strokeWidth(1.0f);    // 선너비 0f : 선없음
            //.fillColor(Color.parseColor("#1AFFFFFF")); // 배경색
            circle = map.addCircle(circle1KM);
 
        } else {
            circle.remove(); // 반경삭제
            circle1KM.center(curPoint);
            circle = map.addCircle(circle1KM);
        }
 
 
    }
 
    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        AutoPermissions.Companion.parsePermissions(this, requestCode, permissions, this);
        Toast.makeText(this"requestCode : "+requestCode+"  permissions : "+permissions+"  grantResults :"+grantResults, Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public void onDenied(int requestCode, String[] permissions) {
        Toast.makeText(getApplicationContext(),"permissions denied : " + permissions.length, Toast.LENGTH_SHORT).show();
    }
 
    @Override
    public void onGranted(int requestCode, String[] permissions) {
        Toast.makeText(getApplicationContext(),"permissions granted : " + permissions.length, Toast.LENGTH_SHORT).show();
    }
}
 
cs

일단 AutoPermissionsListener 를 implement 하여 권한기능을 좀더 편하게 할 수 있도록 하였다.

259라인부터 273라인까지의 메소드 오버라이딩하여 구현하였다.

 

59라인에서

LocationManager manager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

LocationManager 객체인 manager 를 구했고, 이를 바탕으로

 

81라인의 button2 를 클릭하면 startLocationService() 함수가 동작한다. 이 메소드 안의 핵심은 115라인

long minTime = 1000; // 1초,

minTime = 0;            //  매초마다

float minDistance = 0; // 0 m 거리이동시마다.

 

manager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, minTime, minDistance, gpsListener); 

이부분이다. 

 

gpsListener 는 148라인의 GPSListener 의 객체를 삽입하였다. 

GPSListener 의 152라인에서 onLocationChanged() 함수를 통하여 위치가 확인되고 변경되는 새로운 Location location 값을 콜백받게 되고 이를 통해 매번 새로운 위치값을 받을 수 있다. 

 

또한 236라인처럼

myMarker.remove(); // 마커삭제  

이미 객체가 생성되어 있을 경우에 일단 그 마커를 삭제하고 새롭게 변경된 위치에 마커가 표시될 수 있도록 하였다.

 

마찬가지로 현재위치점에서 1Km 반경에 해당하는 새로운 원이 250라인처럼 

circle.remove(); // 반경삭제 

기존의 반경관련 객체가 있을 경우에 삭제하고 새로운 위치점을 기준으로 새반경이 나타나도록 하였다.

 

 

참고 소스 : Do it ! 안드로이드 앱프로그래밍 - 정재곤 님의 소스를 좀더 개량하고 추가 보완하였습니다.

Comments