Day 23:获取位置权限

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 UdemyYoutube 频道。另外,想追踪更多相关技术资讯,欢迎到 脸书粉丝专页 按赞追踪喔~

程序码范例

范例名称:获取位置权限
开发人员:HKT (侯光灿)
程序语言:Kotlin
开发环境:Android Studio 4.1.2 & Android 11 & Kotlin 1.4.30
授权范围:使用时必须注明出处且不得为商业目的之使用
范例下载点:点我下载

在之前的介绍当中,我们主要的重心是放在,如何在滚动式列表上呈现药局资讯。而在接下来的重心,我们将会放在,如何在 Google Map 地图上,呈现药局地点与药局相关资讯。将会依序的跟大家介绍:

  1. 如何获取装置位置权限
  2. 如何检查 GPS 状态
  3. 如何获取装置经纬度座标位置
  4. 如何在 Google Map 上显示药局相关资讯

所以今天要先来跟大家介绍第一个部分,如何获取使用者位置权限,除了要在 AndroidManifest.xml 宣告外,因为「位置权限」被列属为危险权限,从 Android 6 (SDK 23)以上,需要特别额外跟使用询问获取,否则会因权限不足,而造成 APP 闪退。详细权限级别,可以查阅官网文件: Manifest.permission

获取 APP 权限方法

获取 APP 权限,主要分成这四个部分。

checkSelfPermission

检查我们的APP,是否获得权限

shouldShowRequestPermissionRationale

是否要显示更多说明解释为何此权限对话视窗。收到 true,代表需要跟使用者显示更多说明解释为何此权限对话视窗,收到 false,代表不需额外显示给使用者说明。

使用情境:

  • 第一次询问权限,这个值会收到 false。
  • 第一次询问,用户选择「拒绝」,这个值会收到 true。则需要显示客制对话视窗,在视窗中解释为何需要此权限。
  • 用户允许权限後或是选择「拒绝且不在询问」後,这个值会收到 false。

requestPermissions

要求用户给我们APP使用权限

onRequestPermissionsResult

覆写此方法,要求权限後,会收到用户决定是否给权限的结果 CallBack

AndroidManifest.xml

获取位置权限,需要先在 AndroidManifest.xml 里,加入此宣告。位置权限有两种,一个是概略位置存取权(ACCESS_COARSE_LOCATION)和另一个精确位置存取(ACCESS_FINE_LOCATION),择一即可,我们选择精确定位权限。

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

MapActivity.kt

我们先新开一个空白的 Activity 页面,档名为 MapActivity,并在 AndroidManifest.xml 里将启动载入Activity,更改为此 Activity。

<activity android:name=".MapActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>

检查有无权限

透过 checkSelfPermission 方法,检查有无精准位置(ACCESS_FINE_LOCATION)权限,若没有则询问使用者要求获取权限,若获得 locationPermissionGranted 设定为 true。

private var locationPermissionGranted = false

...
...
...

private fun getLocationPermission() {
    //检查权限
    if (ActivityCompat.checkSelfPermission(
            this,
            Manifest.permission.ACCESS_FINE_LOCATION
        ) == PackageManager.PERMISSION_GRANTED
    ) {
        //已获取到权限
        Toast.makeText(this, "已获取到位置权限,可以准备开始获取经纬度", Toast.LENGTH_SHORT).show()
        locationPermissionGranted = true
        //todo checkGPSState()
    } else {
        //询问要求获取权限
        requestLocationPermission()
    }
}

询问要求获取权限

private fun requestLocationPermission() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(
            this, Manifest.permission.ACCESS_FINE_LOCATION
        )
    ) {
        AlertDialog.Builder(this)
            .setMessage("此应用程序,需要位置权限才能正常使用")
            .setPositiveButton("确定") { _, _ ->
                ActivityCompat.requestPermissions(
                    this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION),
                    REQUEST_LOCATION_PERMISSION
                )
            }
            .setNegativeButton("取消") { _, _ -> requestLocationPermission() }
            .show()
    } else {
        ActivityCompat.requestPermissions(
            this, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION), REQUEST_LOCATION_PERMISSION
        )
    }
}

处理权限 CallBack

override fun onRequestPermissionsResult(
    requestCode: Int, permissions: Array<out String>, grantResults: IntArray
) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults)

    when (requestCode) {
        REQUEST_LOCATION_PERMISSION -> {
            if (grantResults.isNotEmpty()) {
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    //已获取到权限
                    locationPermissionGranted = true
                    //todo checkGPSState()
                } else if (grantResults[0] == PackageManager.PERMISSION_DENIED) {
                    if (!ActivityCompat.shouldShowRequestPermissionRationale(
                            this,
                            Manifest.permission.ACCESS_FINE_LOCATION
                        )
                    ) {
                        //权限被永久拒绝
                        Toast.makeText(this, "位置权限已被关闭,功能将会无法正常使用", Toast.LENGTH_SHORT).show()

                        AlertDialog.Builder(this)
                            .setTitle("开启位置权限")
                            .setMessage("此应用程序,位置权限已被关闭,需开启才能正常使用")
                            .setPositiveButton("确定") { _, _ ->
                                val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
                                startActivityForResult(intent, REQUEST_LOCATION_PERMISSION)
                            }
                            .setNegativeButton("取消") { _, _ -> requestLocationPermission() }
                            .show()
                    } else {
                        //权限被拒绝
                        Toast.makeText(this, "位置权限被拒绝,功能将会无法正常使用", Toast.LENGTH_SHORT).show()
                        requestLocationPermission()
                    }
                }
            }
        }
    }
}

处理设定回来 CallBack

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
        REQUEST_LOCATION_PERMISSION -> {
            getLocationPermission()
        }
    }
}

输出结果

第一次呈现询问画面

若在第一次询问,选择「拒绝」,则会出现,我们自定义的对话视窗,可以在这个视窗跟用户说明为何需要权限。

若是选择「拒绝且不在询问」,将不会在出现询问视窗

参考资料

HKT 线上教室
https://tw-hkt.blogspot.com/

Freepik
https://www.freepik.com/

Build location-aware apps
https://developer.android.com/training/location

Request App Permissions
https://developer.android.com/training/permissions/requesting

permissions API reference page
https://developer.android.com/reference/android/Manifest.permission#ACCESS_FINE_LOCATION

Location Data
https://developers.google.com/maps/documentation/android-sdk/location

Select Current Place and Show Details on a Map
https://developers.google.com/maps/documentation/android-sdk/current-place-tutorial


那今天【iThome 铁人赛】就介绍到这边罗~

顺带一提,KT 线上教室,脸书粉丝团,会不定期发布相关资讯,不想错过最新资讯,不要忘记来按赞,追踪喔!也欢迎大家将这篇文章分享给更多人喔。

我们明天再见罗!!!掰掰~


<<:  每个人都该学的30个Python技巧|技巧 23:方便的运算函式—pow()、divmod()、round()(字幕、衬乐、练习)

>>:  Logger 与 Extension Generator for Kotlin

Day 11:「动起来!动起来!」- 用 Tailwind 简单做出过渡和动画效果

还记得我们在之前做过变化模式吗? 没错,就是滑鼠悬停之後会变色的那个。 我们今天呢,就是要来帮它们...

EP16 - 用生活化的例子解释容器,是否搞错了些什麽

容器化是应用程序级别的虚拟化, 允许单个内核上有多个独立的用户空间实体, 而这些实体称为容器。 20...

Day28 订单 -- 定期定额

在好几年前串接金流的时候,还有没定期定额的选项, 记得那时接触的是团体的捐款网站要串接定期定额的功能...

[Day27] CH13:画出你的藏宝图——图形使用者介面

终於来到我们最後一个主题了,今天我们要介绍的是图形使用者介面(graphical user inte...

Component 鬼牌(一): 看 props 决定 Component

鬼牌,在此借用的意思是「可以成为任何一张牌」 Dynamic Components 可以当鬼牌 Dy...