Android App

googleMapApplication (현재 위치, marker 새로 만들기) in Android Studio

YunSeong 2021. 9. 24. 01:24
728x90
반응형

https://yunseong.tistory.com/entry/Android-App-Study19-google-map-api-%EC%82%AC%EC%9A%A9%ED%95%98%EA%B8%B0

 

Android App Study_19 (google map api 사용하기)

https://youtu.be/d-Dkb2bmcQs 1. Maps SDK for Android 연동하기 https://console.cloud.google.com/ Google Cloud Platform 하나의 계정으로 모든 Google 서비스를 Google Cloud Platform을 사용하려면 로그인하..

yunseong.tistory.com

일단 위 페이지에서처럼 google map api를 사용했고 기본적인 코딩을 했다.


1. build.gradle (:app)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
                ...
dependencies {
    implementation 'androidx.appcompat:appcompat:1.3.1'
    implementation 'com.google.android.material:material:1.4.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.0'
    implementation 'org.jetbrains:annotations:15.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.3'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
 
    implementation 'com.google.android.gms:play-services-maps:17.0.0'
    implementation 'com.google.android.gms:play-services-location:17.0.0'
 
    implementation 'gun0912.ted:tedpermission:2.0.0'
}
cs

11~12 - googleMapApi를 사용하기 위한 라이브러리

14 - 권한 허가를 위한 라이브러리


2. gradle.properties

1
2
3
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
android.useAndroidX=true
android.enableJetifier=true
cs

3 - 라이브러리를 추가함으로써 생기는 에러를 해결하기 위해서 위 열을 추가해준다. (추가한 라이브러리를 androidx를 형태로 변환하는 듯하다.)


3. 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
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.googlemapapplication">
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.GoogleMapApplication">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="~~~~~~~~~~~~~~~~~~~~~~"/>
    </application>
</manifest>
cs

4~5 - gps로 사용자의 위치를 불러오기 위해서 추가해준다.

20~22 - googleMapApi를 사용하기 위해서 metaData를 추가해준다.


4. activity_main.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
51
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    tools:context=".MainActivity"
    android:orientation="vertical"
    android:id="@+id/rootLayout"
    android:background="#000000">
    <fragment
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/googleMap"
        class="com.google.android.gms.maps.MapFragment"/>
    <Button
        android:id="@+id/btn_toMyLocation"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginEnd="16dp"
        android:layout_marginRight="16dp"
        android:text="현위치"
        app:layout_constraintEnd_toEndOf="@id/googleMap"
        app:layout_constraintTop_toTopOf="parent" />
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toTopOf="parent"
        android:id="@+id/linearLayout"
        android:layout_marginTop="100dp"
        android:background="#FFFFFF"
        android:orientation="vertical">
        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Name"
            android:textSize="30sp"/>
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="ex) name"
            android:id="@+id/et_name"/>
        <Button
            android:layout_marginTop="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="생성"
            android:id="@+id/btnForApply"/>
    </LinearLayout>
</RelativeLayout>
cs

11~15 - googleMap을 위한 fragment이다.

16~25 - 현위치시점을 움직이는 버튼이다.

26~50 - LinearLayout으로 marker에 대한 정보를 등록하거나 수정할 수 있는 위젯들이 담을 viewGroup이다. 드래그를 통해서 아래로 밀어넣었다가 다시 위로 꺼낼 수 있게 할 것이다.

        39~43 - marker의 title을 입력받을 editText 위젯이다.

        44~49 - 입력한 것을 적용하기 위한 버튼이다. 상황에 따라서 text를 바꿀 것이다.


5. 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
package com.example.googlemapapplication;
 
import ...
 
public class MainActivity extends AppCompatActivity implements OnMapReadyCallback { //implements 중요
 
    private FragmentManager fragmentManager; //googleMap을 띄우기 위한 것
    private MapFragment mapFragment;
 
    private Thread thread;
    private LocationManager locationManager; //현위치를 가져오기 위한 것
    Location location;
    double lattitude;
    double longitude;
 
    private Boolean isFine = false
 
    private RelativeLayout rootLayout; //여러 view들을 연결해주기 위한 것
    private LinearLayout linearLayout;
    private Button btn_toMyLocation;
    private EditText et_name;
    private Button btnForApply;
 
    private boolean isForApply = true//btnForApply를 위한 boolean
    private LatLng latLngForMarker; //marker을 만들 때 사용할 것
 
    GoogleMap map; //googleMap에 대한 것들을 저장할 것
    private boolean onMarker = true//현위치에 시점을 고정하는 것에 대한 boolean
    Marker marker;
    Marker markerForRetouch;
 
    private final static String TAG = "MainActivity.java";
    private final static int UPDATELOCATION = 0;
 
    private int mpHeight; //linearLayout을 움직이게 하기 위한 변수
    private int lastY, curY;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        TedPermission.with(this//권한을 얻기 위한 코드이다.
                .setPermissionListener(permission)
                .setRationaleMessage("위치 확인를 위하여 권한을 허용해주세요.")
                .setDeniedMessage("권한이 거부되었습니다. 설정 > 권한에서 허용할 수 있습니다.")
                .setPermissions(Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION)
                .check();
 
        fragmentManager = getFragmentManager(); 
        mapFragment = (MapFragment) fragmentManager.findFragmentById(R.id.googleMap);
        mapFragment.getMapAsync(this); //googleMap을 사용하기 위한 것
 
        rootLayout = (RelativeLayout)findViewById(R.id.rootLayout);
        linearLayout = (LinearLayout)findViewById(R.id.linearLayout);
        et_name = (EditText)findViewById(R.id.et_name);
 
        btnForApply = (Button)findViewById(R.id.btnForApply);
        btnForApply.setOnClickListener(v -> {
            if (isForApply) {
                if (et_name.getText().toString() == "") { //입력된 것이 없을 때의 부분
                    Toast.makeText(getApplicationContext(), "이름을 입력해주세요.", Toast.LENGTH_SHORT).show();
                } else {
                    MarkerOptions markerOptions = new MarkerOptions(); //marker를 등록하기 위한 부분
                    markerOptions.title(et_name.getText().toString());
                    markerOptions.position(latLngForMarker);
                    map.addMarker(markerOptions);
                    isForApply = false;
                    btnForApply.setText("수정");
                }
            } else {//marker을 수정하기 위한 부분
                markerForRetouch.setTitle(et_name.getText().toString());
            }
        });
 
        btn_toMyLocation = (Button)findViewById(R.id.btn_toMyLocation);
        btn_toMyLocation.setOnClickListener(v -> { //버튼으로 현위치로 시점을 움직이기 위한 부분
            onMarker = true//시점을 현위치에 고정
            btn_toMyLocation.setVisibility(View.INVISIBLE); //버튼을 보이지 않게
            Log.d(TAG, ("onClickListener : onMarker = " + onMarker));
            LatLng location = new LatLng(lattitude, longitude);
            map.moveCamera(CameraUpdateFactory.newLatLng(location));
        });
 
        thread = new Thread() { //계속 위치를 받아오기 위한 thread
            @SuppressLint("MissingPermission")
            @Override
            public void run() {
                while (true) {
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    if (isFine) { //관련 허가를 받았는지 확인해주는 부분
                        location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        lattitude = location.getLatitude(); //gps로 얻은 위치로 좌표를 받아와서 저장한다.
                        longitude = location.getLongitude();
                    }
                    handler.sendEmptyMessage(UPDATELOCATION); // mainThread에서 실행해야할 부분을 실행
                    Log.d(TAG, "run : threadIsRunning");
                }
            }
        };
        thread.start();
 
        linearLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) { // linearLayout을 드레그로 움직일 수 있게 한다.
                RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) linearLayout.getLayoutParams();
                int action = event.getAction();
                if(action == MotionEvent.ACTION_DOWN){
                    lastY = (int)event.getY() + layoutParams.topMargin;
                } else if (action == MotionEvent.ACTION_MOVE && Math.abs(lastY - layoutParams.topMargin) < 100 && lastY < (mpHeight - 200&& lastY > 100) {
                    curY = (int)event.getY() + layoutParams.topMargin;
                    layoutParams.setMargins(0, (layoutParams.topMargin +curY-lastY), 00);
                    linearLayout.setLayoutParams(layoutParams);
                    lastY = curY;
                }
                return true;
            }
        });
    }
 
    @Override
    public void onMapReady(@NonNull @org.jetbrains.annotations.NotNull GoogleMap googleMap) {
        LatLng location = new LatLng(00); //지도에 marker을 만들기 위한 좌표
 
        MarkerOptions markerOptions = new MarkerOptions();
        markerOptions.title("현위치");
        markerOptions.draggable(true);
        markerOptions.position(location);
 
        marker = googleMap.addMarker(markerOptions); //위에서 설정한 marker을 적용
 
        googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(location, 16)); //marker 위치로 camera를 이동하고 줌함
 
        googleMap.setOnCameraMoveListener(new GoogleMap.OnCameraMoveListener() {
            @Override
            public void onCameraMove() {
 
                Log.d(TAG, "onCameraMove : TouchLinstenerIsRunning");
 
                if (Math.abs(map.getCameraPosition().target.latitude - lattitude) < 0.0005 && Math.abs(map.getCameraPosition().target.longitude - longitude) < 0.0005) {
                    onMarker = true//시점이 현위치에 주변에 가면 고정한다.
                    btn_toMyLocation.setVisibility(View.INVISIBLE);
                    Log.d(TAG, ("onCameraMove : onMarker = " + onMarker));
                } else {
                    onMarker = false//시점이 현위치를 벗어나면 고정을 푼다.
                    btn_toMyLocation.setVisibility(View.VISIBLE);
                    Log.d(TAG, ("onCameraMove : onMarker = " + onMarker));
                }
            }
        });
 
        googleMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
            @Override
            public boolean onMarkerClick(Marker marker) {
                if (marker.getTitle().toString() != "현위치"){ //marker을 클릭했을 때 그 marker을 수정할 수 있게 준비한다.
                    markerForRetouch = marker;
                    et_name.setText(marker.getTitle());
                } else  {
                    onMarker = true;
                    btn_toMyLocation.setVisibility(View.INVISIBLE);
                }
                googleMap.moveCamera(CameraUpdateFactory.newLatLng(new LatLng(marker.getPosition().latitude, marker.getPosition().longitude)));
 
                return true;
            }
        });
 
        googleMap.setOnMapLongClickListener(new GoogleMap.OnMapLongClickListener() { //오래 터치했을 때 marker을 생성할 준비를 한다.
            @Override
            public void onMapLongClick(LatLng latLng) {
                googleMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));
                RelativeLayout.LayoutParams layoutParams = (RelativeLayout.LayoutParams) linearLayout.getLayoutParams();
                isForApply = true;
                btnForApply.setText("생성");
                layoutParams.setMargins(0, mpHeight/2+10000); //정보를 입력받기 위한 linearLayout을 적당한 높이로 띄운다.
                linearLayout.setLayoutParams(layoutParams);
                btn_toMyLocation.setVisibility(View.VISIBLE);
                onMarker = false;
                latLngForMarker = latLng;
            }
        });
 
        map = googleMap;
    }
 
    PermissionListener permission = new PermissionListener() {
        @Override
        public void onPermissionGranted() {
            Toast.makeText(MainActivity.this"권한 허가", Toast.LENGTH_SHORT);
            locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
            isFine = true//허가를 받았을 때 그 정보를 저장
        }
 
        @Override
        public void onPermissionDenied(ArrayList<String> deniedPermissions) {
            Toast.makeText(MainActivity.this"권한 거부", Toast.LENGTH_SHORT);
        }
    };
 
    private Handler handler = new Handler() { //thread에서 사용될 부분을 정의
        @Override
        public void handleMessage(@NonNull Message msg) {
            if (msg.what == UPDATELOCATION){
                LatLng location = new LatLng(lattitude, longitude);
                marker.setPosition(location);
                Log.d(TAG, ("handleMessage: onMarker = " + onMarker));
                if (onMarker) map.moveCamera(CameraUpdateFactory.newLatLng(location));//현위치에 시점이 고정되어있을 때 시점을 marker로 이동
            } 
        }
    };
 
    @Override
    public void onWindowFocusChanged(boolean hasFocus) {
        super.onWindowFocusChanged(hasFocus);
        mpHeight = rootLayout.getHeight(); //현재 화면의 세로 방향의 px를 저장함
    }
}
cs

6. 결과

 

728x90
반응형