dependencies {
// Google Map
implementation("com.google.android.gms:play-services-maps:17.0.0")
implementation("com.google.android.gms:play-services-location:17.0.0")
}






SHA-1 얻는 방법
맥 기준 => 터미널에서 다음 명령어 입력
keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="발급받은 API 키 넣기" />



<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="23dp"
android:layout_marginBottom="30dp"
android:backgroundTint="@color/skyBlue"
android:src="@drawable/icon_map"
app:borderWidth="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:shapeAppearanceOverlay="?attr/shapeAppearanceCornerExtraLarge"
app:tint="@color/white" />
🚨 모양 설정을 하지 않은 기본이 버튼이 다음 사진과 같이 네모난 모양의 버튼으로 뜨는 경우
- app:shapeAppearanceOverlay를 추가해 ?attr/shapeAppearanceCornerExtraLarge 속성을 넣으면 동그란 버튼이 됨
[drawable] ➡️ [New] ➡️ [Drawable Resource File]

Root element를 shape로 설정하고 OK

다음 코드 작성
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners
android:radius="10dp"/>
<solid
android:color="@color/skyBlue_transparent" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/fab_current_location"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginBottom="12dp"
android:backgroundTint="@color/white"
android:src="@drawable/icon_location"
android:tint="@color/skyBlue"
app:borderWidth="0dp"
app:layout_constraintBottom_toTopOf="@+id/btn_check_here"
app:layout_constraintEnd_toEndOf="parent"
app:shapeAppearanceOverlay="?attr/shapeAppearanceCornerExtraLarge" />
<LinearLayout
android:id="@+id/btn_check_here"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginStart="20dp"
android:layout_marginEnd="20dp"
android:layout_marginBottom="20dp"
android:background="@drawable/bg_button_blue"
android:gravity="center"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent">
<ImageView
android:layout_width="25dp"
android:layout_height="25dp"
android:src="@drawable/icon_search"
app:tint="@color/white" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:letterSpacing="-0.035"
android:text="여기 미세먼지 농도 측정"
android:textColor="@color/white"
android:textSize="12sp"
android:textStyle="bold" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
class MainActivity : AppCompatActivity() {
// 위도, 경도 저장
var latitude: Double = 0.0
var longitude: Double = 0.0
private fun updateUI() {
if (latitude == 0.0 || longitude == 0.0) {
// 위도와 경도 정보를 가져옴
latitude = locationProvider.getLocationLatitude()
longitude = locationProvider.getLocationLongitude()
}
}
}
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.airquality_app.databinding.ActivityMapBinding
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.LatLng
import com.google.android.gms.maps.model.MarkerOptions
class MapActivity : AppCompatActivity(), OnMapReadyCallback {
lateinit var binding: ActivityMapBinding
private var mMap: GoogleMap? = null
var currentLat: Double = 0.0
var currentLng: Double = 0.0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMapBinding.inflate(layoutInflater)
setContentView(binding.root)
// MainActivity에서 전달된 값 가져옴
currentLat = intent.getDoubleExtra("currentLat", 0.0)
currentLng = intent.getDoubleExtra("currentLng", 0.0)
// SupportMapFragment 객체를 mapFragment에 저장
// SupportMapFragment: 구글 맵 객체의 생명주기를 관리하는 객체
val mapFragment = supportFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
// getMapAsync(): mapFragment에 OnMapReadyCallback 인터페이스를 등록해줌 => 지도가 준비되면 onMapReady()함수 자동 실행
mapFragment?.getMapAsync(this)
binding.btnCheckHere.setOnClickListener {
mMap?.let {
val intent = Intent()
// 버튼이 눌린 시점의 카메라 포지션 가져옴(보이는 지도의 중앙지점 좌푯값 가져옴)
intent.putExtra("latitude", it.cameraPosition.target.latitude)
intent.putExtra("longitude", it.cameraPosition.target.longitude)
// setResult() 함수: MainActivity.kt에서 정의해두었던 onActivityResult() 함수 실행
setResult(Activity.RESULT_OK, intent)
finish() // 지도 액티비티 종료
}
}
}
// 지도가 준비되었을 때 실행되는 콜백
override fun onMapReady(googleMap: GoogleMap) {
mMap = googleMap
mMap?.let {
val currentLocation = LatLng(currentLat, currentLng)
it.setMaxZoomPreference(20.0f) // 줌 최댓값 설정
it.setMinZoomPreference(12.0f) // 줌 최솟값 설정
it.moveCamera(CameraUpdateFactory.newLatLngZoom(currentLocation, 16f))
}
setMarker()
// 플로팅 버튼이 눌렸을 때 현재 위도, 경도 정보를 가져와 지도의 위치를 움직임
binding.fabCurrentLocation.setOnClickListener {
val locationProvider = LocationProvider(this@MapActivity)
// 위도와 경도 정보 가져옴
val latitude = locationProvider.getLocationLatitude()
val lontitude = locationProvider.getLocationLongitude()
mMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(latitude, lontitude), 16f))
setMarker()
}
}
// 마커 설정 함수
private fun setMarker() {
mMap?.let {
it.clear() // 지도에 있는 마커 먼저 삭제
val markerOptions = MarkerOptions()
markerOptions.position(it.cameraPosition.target) // 마커의 위치 설정
markerOptions.title("마커 위치") // 마커의 이름 설정
val marker = it.addMarker(markerOptions) // 지도에 마커를 추가하고, 마커 객체를 반환
// setPosition() 함수: 마커를 지도에 추가
// setOnCameraMoveListener() 함수: 지도가 움직일 때 마커도 함께 움직임
it.setOnCameraMoveListener {
marker?.let {
marker -> marker.setPosition(it.cameraPosition.target)
}
}
}
}
}
class MainActivity : AppCompatActivity() {
// 결과를 받아와야 하는 액티비티를 실행할 때 사용하는 변수 선언
// registerForActivityResult(: 다른 액티비티의 실행 결과를 콜백에 등록) 객체 생성
// 콜백은 해당 액티비티가 결과를 반환할 때 실행
val startMapActivityResult = registerForActivityResult(ActivityResultContracts.StartActivityForResult(), object : ActivityResultCallback<ActivityResult> {
override fun onActivityResult(result: ActivityResult) {
if (result?.resultCode ?: 0 == Activity.RESULT_OK) {
// 지도 페이지에서 위도와 경도 반환
latitude = result?.data?.getDoubleExtra("latitude", 0.0) ?: 0.0
longitude = result?.data?.getDoubleExtra("longitude", 0.0) ?: 0.0
updateUI()
}
}
})
override fun onCreate(savedInstanceState: Bundle?)
// 플로팅 액션 버튼 클릭 시
setFab()
}
private fun setFab() {
binding.fab.setOnClickListener {
val intent = Intent(this@MainActivity, MapActivity::class.java)
intent.putExtra("currentLat", latitude)
intent.putExtra("currntLng", longitude)
// startMapActivityResult.launch() : 지도 페이지로 이동하고, 등록해둔 onActivityResult 콜백에 보낸 값이 전달
startMapActivityResult.launch(intent)
}
}
}
