저번 시간에 Java를 통해 사용자의 위치를 TextView에 표시하는 기능을 구현했었습니다.
이번 포스팅에서는 앞선 기능을 Kotlin으로 구현해 보도록 하겠습니다.
개발 순서
1. manifest.xml 권한 추가
2. 화면 그리기 (activity_g_p_s_permission.xml)
3. 의존성 추가
4. 코드 작성(GPSPermissionActivity.kt)
5. 결과물
개발
1. manifest.xml 권한 추가
사용자의 위치를 받아 오기 위해서는 매니페스트에 권한을 추가해줘야 합니다.
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
developer.android.com/reference/android/Manifest.permission
안드로이 개발자 페이지에서 메니페스트 설명을 보면 아래와 같이 적혀있습니다. protection level 이 위험(dangerous) 단계이므로 사용자에게 권한을 요청해야 합니다. 두 권한 중 하나만 manifest에 작성하셔도 무방합니다.
2. 화면 그리기 (activity_g_p_s_permission.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
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=".GPSPermissionActivity">
<Button
android:id="@+id/btn_start_upds"
android:layout_width="match_parent"
android:layout_marginRight="12dp"
android:layout_marginLeft="12dp"
android:layout_marginTop="24dp"
android:layout_height="wrap_content"
android:text="위치 정보 업데이트 시작" />
<Button
android:enabled="false"
android:id="@+id/btn_stop_upds"
android:layout_width="match_parent"
android:layout_marginRight="12dp"
android:layout_marginLeft="12dp"
android:layout_height="wrap_content"
android:text="위치 정보 업데이트 종료" />
<TextView
android:padding="12dp"
android:layout_marginTop="12dp"
android:textSize="18sp"
android:layout_marginRight="12dp"
android:layout_marginLeft="12dp"
android:layout_width="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_height="wrap_content"
android:text="위도"
android:id="@+id/txtLat"
/>
<TextView
android:padding="12dp"
android:layout_gravity="center_horizontal"
android:textSize="18sp"
android:layout_marginRight="12dp"
android:layout_marginLeft="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="경도"
android:id="@+id/txtLong"
/>
<TextView
android:layout_gravity="center_horizontal"
android:padding="12dp"
android:textSize="18sp"
android:layout_marginRight="12dp"
android:layout_marginLeft="12dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="업데이트 시간"
android:id="@+id/txtTime" />
</LinearLayout>
위처럼 레이아웃을 만들었습니다. '위치 정보 업데이트 시작' 버튼을 누르게 되면 일정하게 위치 정보를 받아와 각 위도, 경도, 업데이트 시간 Text 란에 업데이트를 시켜주고 '위치 정보 업데이트 종료' 버튼을 누르게 되면 업데이트를 종료하도록 구현할 예정입니다.
3. 의존성 추가
우선 Location API 사용을 위하여 app.gradle(app)에 의존성을 추가해주었습니다.
4. 코드 작성 (GPSPermissionActivity.kt)
위치 값을 가져오기 위해서는 LocationRequest를 이용합니다.
developer.android.com/training/location/request-updates?hl=ko
기존에 LocationRequest를 생성할 때 생성자 메서드(LocationRequest())를 사용하였는데요. 이 메서드가 deprecated 됨으로써 아래와 같이 메서드를 이용해서 객체를 생성해줍시다.
위치 정보를 받아 오는 코드를 저는 스틱코드를 이용해서 쉽게 구현하였습니다. 크게 변수, 권한, 제어 하는 코드로 불러왔습니다.
stickode.com/detail.html?no=2102
class GPSPermissionActivity : AppCompatActivity() {
val TAG: String = "로그"
private var mFusedLocationProviderClient: FusedLocationProviderClient? = null // 현재 위치를 가져오기 위한 변수
lateinit var mLastLocation: Location // 위치 값을 가지고 있는 객체
internal lateinit var mLocationRequest: LocationRequest // 위치 정보 요청의 매개변수를 저장하는
private val REQUEST_PERMISSION_LOCATION = 10
lateinit var btnStartupdate: Button
lateinit var btnStopUpdates: Button
lateinit var txtLat: TextView
lateinit var txtLong: TextView
lateinit var txtTime: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_g_p_s_permission)
// 화면뷰 inflate.
btnStartupdate = findViewById(R.id.btn_start_upds)
btnStopUpdates = findViewById(R.id.btn_stop_upds)
txtLat = findViewById(R.id.txtLat)
txtLong = findViewById(R.id.txtLong)
txtTime = findViewById(R.id.txtTime)
// LocationRequest() deprecated 되서 아래 방식으로 LocationRequest 객체 생성
// mLocationRequest = LocationRequest() is deprecated
mLocationRequest = LocationRequest.create().apply {
interval = 2000 // 업데이트 간격 단위(밀리초)
fastestInterval = 1000 // 가장 빠른 업데이트 간격 단위(밀리초)
priority = LocationRequest.PRIORITY_HIGH_ACCURACY // 정확성
maxWaitTime= 2000 // 위치 갱신 요청 최대 대기 시간 (밀리초)
}
// 위치 추척 시작 버튼 클릭시 처리
btnStartupdate.setOnClickListener {
if (checkPermissionForLocation(this)) {
startLocationUpdates()
// View Button 활성화 상태 변경
btnStartupdate.isEnabled = false
btnStopUpdates.isEnabled = true
}
}
// 위치 추적 중지 버튼 클릭시 처리
btnStopUpdates.setOnClickListener {
stoplocationUpdates()
txtTime.text = "Updates Stoped"
// View Button 활성화 상태 변경
btnStartupdate.isEnabled = true
btnStopUpdates.isEnabled = false
}
}
protected fun startLocationUpdates() {
Log.d(TAG, "startLocationUpdates()")
//FusedLocationProviderClient의 인스턴스를 생성.
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this)
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() 위치 권한이 하나라도 존재하는 경우")
// 기기의 위치에 관한 정기 업데이트를 요청하는 메서드 실행
// 지정한 루퍼 스레드(Looper.myLooper())에서 콜백(mLocationCallback)으로 위치 업데이트를 요청합니다.
mFusedLocationProviderClient!!.requestLocationUpdates(mLocationRequest, mLocationCallback, Looper.myLooper())
}
// 시스템으로 부터 위치 정보를 콜백으로 받음
private val mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
Log.d(TAG, "onLocationResult()")
// 시스템에서 받은 location 정보를 onLocationChanged()에 전달
locationResult.lastLocation
onLocationChanged(locationResult.lastLocation)
}
}
// 시스템으로 부터 받은 위치정보를 화면에 갱신해주는 메소드
fun onLocationChanged(location: Location) {
Log.d(TAG, "onLocationChanged()")
mLastLocation = location
val date: Date = Calendar.getInstance().time
val simpleDateFormat = SimpleDateFormat("hh:mm:ss a")
txtTime.text = "Updated at : " + simpleDateFormat.format(date) // 갱신된 날짜
txtLat.text = "LATITUDE : " + mLastLocation.latitude // 갱신 된 위도
txtLong.text = "LONGITUDE : " + mLastLocation.longitude // 갱신 된 경도
}
// 위치 업데이터를 제거 하는 메서드
private fun stoplocationUpdates() {
Log.d(TAG, "stoplocationUpdates()")
// 지정된 위치 결과 리스너에 대한 모든 위치 업데이트를 제거
mFusedLocationProviderClient!!.removeLocationUpdates(mLocationCallback)
}
// 위치 권한이 있는지 확인하는 메서드
fun checkPermissionForLocation(context: Context): Boolean {
Log.d(TAG, "checkPermissionForLocation()")
// Android 6.0 Marshmallow 이상에서는 지리 확보(위치) 권한에 추가 런타임 권한이 필요합니다.
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "checkPermissionForLocation() 권한 상태 : O")
true
} else {
// 권한이 없으므로 권한 요청 알림 보내기
Log.d(TAG, "checkPermissionForLocation() 권한 상태 : X")
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_PERMISSION_LOCATION)
false
}
} else {
true
}
}
// 사용자에게 권한 요청 후 결과에 대한 처리 로직
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
Log.d(TAG, "onRequestPermissionsResult()")
if (requestCode == REQUEST_PERMISSION_LOCATION) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d(TAG, "onRequestPermissionsResult() _ 권한 허용 클릭")
startLocationUpdates()
// View Button 활성화 상태 변경
btnStartupdate.isEnabled = false
btnStopUpdates.isEnabled = true
} else {
Log.d(TAG, "onRequestPermissionsResult() _ 권한 허용 거부")
Toast.makeText(this@GPSPermissionActivity, "권한이 없어 해당 기능을 실행할 수 없습니다.", Toast.LENGTH_SHORT).show()
}
}
}
}
5. 결과물
'안드로이드 코틀린' 카테고리의 다른 글
[Kotlin][Android] 안드로이드 - 스낵바(snackbar) (0) | 2021.05.10 |
---|---|
[Kotlin][Android] 바텀 네비게이션바 만들기 (2) | 2021.05.09 |
[Kotlin][Android] 뷰페이저를 사용하여 화면 슬라이드 기능 만들기 (0) | 2021.04.28 |
[Kotlin][Android] Bottom Sheet 만들기 (0) | 2021.04.20 |
[Kotlin][Android] 알림(Notification) 기능 만들기 (1) | 2021.04.17 |