kotlin 编写一个简单的天气预报app (八)获取设备坐标

2024-05-25 06:36

本文主要是介绍kotlin 编写一个简单的天气预报app (八)获取设备坐标,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、请求获取设备坐标的权限

1.1 对应的权限需要申请在AndroidManifest

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

1.2 新建一个LocationManagerUtils类

class LocationManagerUtils(val context: Context, ) {private val permissionRequestCode = 1interface PermissionCallback {fun onPermissionGranted()fun onPermissionDenied()}private var permissionCallback: PermissionCallback? = nullfun setPermissionCallback(callback: PermissionCallback) {permissionCallback = callback}private fun checkLocationPermissions() : Boolean {val fineLocationPermission =ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_FINE_LOCATION)val coarseLocationPermission =ContextCompat.checkSelfPermission(context, Manifest.permission.ACCESS_COARSE_LOCATION)return (fineLocationPermission  == PackageManager.PERMISSION_GRANTED&& coarseLocationPermission == PackageManager.PERMISSION_GRANTED)}fun requestLocationPermission(activity: Activity) {if(checkLocationPermissions()) {ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.ACCESS_FINE_LOCATION,Manifest.permission.ACCESS_COARSE_LOCATION), permissionRequestCode)}}fun onRequestPermissionsResult(requestCode: Int,grantResults: IntArray) {if (requestCode == permissionRequestCode) {var allPermissionsGranted = truefor (result in grantResults) {if (result != PackageManager.PERMISSION_GRANTED) {allPermissionsGranted = falsebreak}}if (!allPermissionsGranted) {permissionCallback?.onPermissionDenied()} else {permissionCallback?.onPermissionGranted()}}}
}

这个LocationManagerUtils类是一个用于处理位置权限和位置更新的实用工具类。

它提供了一种方便的方法来检查和请求位置权限,并通过回调函数来处理权限请求的结果和位置更新。

下面是这个类的一些关键部分的解释:

context: Context: 这是一个构造函数参数,用于获取应用的上下文。

permissionRequestCode = 1: 这是一个私有变量,用于标识权限请求的请求码。

PermissionCallback: 这是一个内部接口,定义了两个个方法:onPermissionGranted()onPermissionDenied()。这些方法分别用于处理权限被授予、权限被拒绝的情况。

permissionCallback: PermissionCallback? = null: 这是一个私有变量,用于存储PermissionCallback的实例。

setPermissionCallback(callback: PermissionCallback): 这是一个公开方法,用于设置PermissionCallback的实例。

checkLocationPermissions(): 这是一个私有方法,用于检查应用是否已经获得了细粒度和粗粒度位置权限。它返回一个布尔值,表示是否已经获得了这两个权限。

requestLocationPermission(activity: Activity): 这是一个公开方法,用于请求位置权限。如果应用尚未获得位置权限,它会调用ActivityCompat.requestPermissions()方法来请求权限。

onRequestPermissionsResult(requestCode: Int, grantResults: IntArray): 这是一个公开方法,用于处理权限请求的结果。它会检查请求码是否匹配,并根据grantResults数组中的结果来调用PermissionCallback的相应方法。

总的来说,这个类提供了一种封装好的方式来处理位置权限的请求和结果,以及位置更新。通过设置一个PermissionCallback实例,你可以在权限被授予或被拒绝时收到通知,并在位置发生变化时收到更新。

1.3 在MainActivity里:

class MainActivity : AppCompatActivity(), LocationManagerUtils.PermissionCallback {private lateinit var locationManagerUtils: LocationManagerUtilsoverride fun onCreate(savedInstanceState: Bundle?) {locationManagerUtils = LocationManagerUtils(this).apply {setPermissionCallback(this@MainActivity)requestLocationPermission(this@MainActivity)}}override fun onRequestPermissionsResult(requestCode: Int,permissions: Array<out String>,grantResults: IntArray) {super.onRequestPermissionsResult(requestCode, permissions, grantResults)locationManagerUtils.onRequestPermissionsResult(requestCode, grantResults)}override fun onPermissionGranted() {Log.d(tag, "onPermissionGranted")locationManagerUtils.requestNetworkLocationUpdates()}override fun onPermissionDenied() {Toast.makeText(this, R.string.requiredPermissionPrompt, Toast.LENGTH_SHORT).show()ActivityCompat.finishAffinity(this)}}

MainActivity中,LocationManagerUtils类被用来处理位置权限和位置更新。这里是MainActivity中使用LocationManagerUtils的步骤:

MainActivity继承了AppCompatActivity并实现了LocationManagerUtils.PermissionCallback接口。这意味着MainActivity需要实现PermissionCallback接口中的方法。

MainActivity中,有一个locationManagerUtils属性,它被延迟初始化(lateinit),这意味着它将在后面被初始化。

onCreate方法中,locationManagerUtils被初始化为一个LocationManagerUtils实例,并通过apply函数设置了权限回调(setPermissionCallback(this@MainActivity))并请求位置权限(requestLocationPermission(this@MainActivity))。

onRequestPermissionsResult方法被重写以处理权限请求的结果。它首先调用父类的onRequestPermissionsResult方法,然后将结果传递给locationManagerUtilsonRequestPermissionsResult方法。

onPermissionGranted方法被实现以处理权限被授予的情况。在这个方法中,日志被记录,并且调用了locationManagerUtilsrequestNetworkLocationUpdates方法来请求网络位置更新。

onPermissionDenied方法被实现以处理权限被拒绝的情况。在这个方法中,一个提示被显示,告诉用户权限被拒绝,并且使用ActivityCompat.finishAffinity方法来结束当前的Activity及其所有关联的任务。

总的来说,MainActivity使用LocationManagerUtils来请求位置权限,并在权限被授予时请求位置更新。如果权限被拒绝,则显示提示并结束Activity

二、获取坐标

class LocationManagerUtils(val context: Context, ) {//...interface PermissionCallback {//...fun onLocationChanged(location: Location)}private val locationManager: LocationManager = context.getSystemService(Context.LOCATION_SERVICE) as LocationManagerprivate val locationListener = object : LocationListener {override fun onLocationChanged(location: Location) {permissionCallback?.onLocationChanged(location)}@Deprecated("Deprecated in Java")override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) {}override fun onProviderEnabled(provider: String) {}override fun onProviderDisabled(provider: String) {}}@SuppressLint("MissingPermission")fun requestNetworkLocationUpdates() {locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,1000L,1000f,locationListener)}
}

这个LocationManagerUtils类是一个用于管理和监听Android设备位置更新的实用工具类。它提供了一种方便的方法来请求网络提供的位置更新,并通过一个回调接口来处理位置变化。下面是这个类的一些关键部分的解释:

locationManager: LocationManager: 这是一个私有属性,用于获取系统的定位服务。

locationListener: 这是一个私有属性,是一个匿名内部类,实现了LocationListener接口。它用于监听位置更新,并在位置发生变化时调用PermissionCallbackonLocationChanged方法。

requestNetworkLocationUpdates(): 这是一个公开方法,用于请求网络提供的位置更新。它使用LocationManagerrequestLocationUpdates方法来注册位置监听器,并设置了更新频率(每1000毫秒或1秒更新一次,位置变化至少1000米时更新)。

总的来说,这个类提供了一种封装好的方式来请求和监听网络提供的位置更新,并通过设置一个PermissionCallback实例来处理位置更新。

在MainAcitivity里,继承onLocationChanged来获取坐标变化

class MainActivity : AppCompatActivity(), LocationManagerUtils.PermissionCallback {override fun onLocationChanged(location: Location) {}
}

三、天气坐标api的添加

3.1 添加opeanweathermap接口

interface WeatherService {/* https://openweathermap.org/current */@GET("weather")fun getWeatherByCityName(@Query("q") cityName : String,@Query("appid") apiKey : String) : Call<WeatherResponse>@GET("weather")fun getWeatherByLocation(@Query("lat") latitude: Double,@Query("lon") longitude: Double,@Query("appid") apkKey: String) : Call<WeatherResponse>/* https://openweathermap.org/forecast5 */@GET("forecast")fun getForecastByCityName(@Query("q") cityName : String,@Query("appid") apiKey : String) : Call<ForecastResponse>@GET("forecast")fun getForecastByLocation(@Query("lat") latitude: Double,@Query("lon") longitude: Double,@Query("appid") apkKey: String) : Call<ForecastResponse>}

WeatherService 接口定义了一组用于从天气服务 API(如 OpenWeatherMap)访问天气和预报数据的方法。以下是这些方法及其参数的详细说明:

getWeatherByCityName:此方法通过城市名称获取当前天气数据。它需要两个参数:cityNameapiKeycityName 是城市的名称,apiKey 是进行身份验证所需的 API 密钥。

getWeatherByLocation:此方法通过纬度和经度获取当前天气数据。它需要三个参数:latitudelongitudeapkKey(应该是 apiKey)。latitudelongitude 指定位置的坐标,apkKey 是进行身份验证所需的 API 密钥。

getForecastByCityName:此方法通过城市名称获取预报数据。它需要两个参数:cityName 和 apiKey。cityName 是城市的名称,apiKey 是进行身份验证所需的 API 密钥。

getForecastByLocation:此方法通过纬度和经度获取预报数据。它需要三个参数:latitude、longitude 和 apkKey(应该是 apiKey)。latitudelongitude 指定位置的坐标,apkKey 是进行身份验证所需的 API 密钥。

每个方法都返回一个泛型类型为 WeatherResponseForecastResponse 的 Call 对象,具体取决于它是当前天气数据还是预报数据。这些 Call 对象代表可以入队的异步请求,可以执行网络请求并提供响应或潜在错误的回调。

3.2 在RetrofitClient里添加根据坐标获取天气的接口

object RetrofitClient {private fun callGetCurrentWeather(call: Call<WeatherResponse>) {call.enqueue(object : Callback<WeatherResponse> {override fun onResponse(call: Call<WeatherResponse>,response: Response<WeatherResponse>) {if (response.isSuccessful) {val weatherData = response.body()handleWeatherData(weatherData)} else {handleWeatherFailure(response.message())}}override fun onFailure(call: Call<WeatherResponse>, t: Throwable) {handleWeatherFailure(t.message!!)}})}fun getWeatherByCityName(cityName: String) {val call = weatherService.getWeatherByCityName(cityName, API_KEY)callGetCurrentWeather(call)}fun getWeatherByLocation(location: Location) {val call = weatherService.getWeatherByLocation(location.latitude, location.longitude, API_KEY)Log.d(TAG, "lat:${location.latitude},lon:${location.longitude}")callGetCurrentWeather(call)}private fun callGetForecast(call: Call<ForecastResponse>) {call.enqueue(object : Callback<ForecastResponse> {override fun onResponse(call : Call<ForecastResponse>, response: Response<ForecastResponse>) {if(response.isSuccessful) {val forecastData = response.body()handleForecastData(forecastData)} else {handleForecastFailure(response.message())}}override fun onFailure(call: Call<ForecastResponse>, t: Throwable) {handleForecastFailure(t.message!!)}})}fun getForecastByCityName(cityName: String) {val call = weatherService.getForecastByCityName(cityName, API_KEY)callGetForecast(call)}fun getForecastByLocation(location: Location) {val call = weatherService.getForecastByLocation(location.latitude, location.longitude, API_KEY)callGetForecast(call)}//...
}

其他都一样的,只是参数改成了Location

四、获取最近城市的信息

4.1 在CityDataAdapter获取最近的城市数据

class CityDataAdapter(private val originCityDataList: List<CityData>) :RecyclerView.Adapter<CityDataAdapter.ViewHolder>() {fun getClosestCityName(deviceLocation: Location) : CityData? {var closestCityData : CityData ?= nullvar minDistance = Double.MAX_VALUEfor(cityData in originCityDataList) {val cityLocation = Location("")cityLocation.latitude = cityData.coord.latcityLocation.longitude = cityData.coord.lonval distance = deviceLocation.distanceTo(cityLocation)if(distance < minDistance) {minDistance = distance.toDouble()closestCityData = cityData}}Log.d("CityDataAdapter", "city name: ${closestCityData?.name}, " +"${closestCityData?.coord?.lat},${closestCityData?.coord?.lon}, "+"distance:${minDistance}")return closestCityData}
}

这个CityDataAdapter类是一个为RecyclerView设计的适配器,用于展示一个城市数据列表。它包含了一个方法getClosestCityName,用于根据设备的位置找到最近的城市的功能。这里是这个类的详细描述:

originCityDataList: List<CityData>: 这是一个构造函数参数,用于接收一个城市数据列表。

getClosestCityName(deviceLocation: Location) : CityData?: 这是一个方法,用于找到与设备位置最近的城市的城市数据。它接受一个Location对象作为参数,表示设备的位置。

closestCityData: CityData? = null: 这是一个局部变量,用于存储最近的城市的城市数据。

minDistance = Double.MAX_VALUE: 这是一个局部变量,用于存储最近城市的最小距离。

方法中的for循环遍历originCityDataList中的每个城市数据,并创建一个Location对象来表示城市的位置。

使用distanceTo方法计算设备位置与城市位置之间的距离。

如果计算出的距离小于minDistance,则更新minDistanceclosestCityData

方法最后,通过日志输出最近城市的相关信息,并返回closestCityData

总的来说,这个适配器不仅用于展示城市数据列表,还提供了一个方便的方法来找到用户设备位置最近的城市的城市数据。

4.2 在MainActivity里补全:

class MainActivity : AppCompatActivity(), LocationManagerUtils.PermissionCallback {override fun onLocationChanged(location: Location) {val cityDataAdapter = binding.cityDataRecyclerView.adapter as CityDataAdapterval closestCityData = cityDataAdapter.getClosestCityName(location)val locationMessage = "latitude:${location.latitude}, longitude:${location.longitude}"val cityName = "closet CityName:${closestCityData?.name}"val message = "$locationMessage, $cityName"if(closestCityData == null) {RetrofitClient.getWeatherByLocation(location)RetrofitClient.getForecastByLocation(location)Toast.makeText(this, "update by location $locationMessage", Toast.LENGTH_SHORT).show()} else {val name = closestCityData.nameRetrofitClient.getWeatherByCityName(name)RetrofitClient.getForecastByCityName(name)Toast.makeText(this, "update by city name $cityName", Toast.LENGTH_SHORT).show()}Log.d(tag, message)}
}

在MainActivity中,实现了LocationManagerUtils.PermissionCallback接口的onLocationChanged方法。这个方法会在设备的位置发生变化时被调用,用于根据新的位置信息更新天气数据。这里是这个方法的详细描述:

cityDataAdapter = binding.cityDataRecyclerView.adapter as CityDataAdapter: 这行代码获取了与RecyclerView绑定的适配器,并将其转换为CityDataAdapter类型。

closestCityData = cityDataAdapter.getClosestCityName(location): 这行代码调用了CityDataAdapter的getClosestCityName方法,传入了当前设备的位置信息,以获取最近的城市的城市数据。

locationMessage = "latitude:${location.latitude}, longitude:${location.longitude}": 这行代码创建了一个包含当前位置信息的字符串。

cityName = "closet CityName:${closestCityData?.name}": 这行代码创建了一个包含最近城市的城市名称的字符串。

message = "$locationMessage, $cityName": 这行代码将位置信息和城市名称合并为一个消息字符串。

如果closestCityDatanull,表示没有找到最近的城市的城市数据,那么将通过位置信息来更新天气数据:

  • RetrofitClient.getWeatherByLocation(location):
    这行代码调用RetrofitClientgetWeatherByLocation方法,传入了当前设备的位置信息,以获取天气数据。
  • RetrofitClient.getForecastByLocation(location):
    这行代码调用RetrofitClientgetForecastByLocation方法,传入了当前设备的位置信息,以获取天气预报数据。
  • 显示一个提示,告诉用户天气数据正在通过位置信息更新。

如果closestCityData不为null,表示找到了最近的城市的城市数据,那么将通过城市名称来更新天气数据:

  • name = closestCityData.name: 这行代码获取了最近城市的城市名称。
  • RetrofitClient.getWeatherByCityName(name): 这行代码调用RetrofitClientgetWeatherByCityName方法,传入了最近城市的城市名称,以获取天气数据。
  • RetrofitClient.getForecastByCityName(name): 这行代码调用RetrofitClientgetForecastByCityName方法,传入了最近城市的城市名称,以获取天气预报数据。
  • 显示一个提示,告诉用户天气数据正在通过城市名称更新。
  • Log.d(tag, message): 这行代码记录了一个日志,包含了位置信息和城市名称的消息。

总的来说,这个onLocationChanged方法用于根据设备的位置信息来更新天气数据,并通过提示和日志来通知用户更新情况。

这篇关于kotlin 编写一个简单的天气预报app (八)获取设备坐标的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1000743

相关文章

python获取指定名字的程序的文件路径的两种方法

《python获取指定名字的程序的文件路径的两种方法》本文主要介绍了python获取指定名字的程序的文件路径的两种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 最近在做项目,需要用到给定一个程序名字就可以自动获取到这个程序在Windows系统下的绝对路径,以下

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

Kotlin 枚举类使用举例

《Kotlin枚举类使用举例》枚举类(EnumClasses)是Kotlin中用于定义固定集合值的特殊类,它表示一组命名的常量,每个枚举常量都是该类的单例实例,接下来通过本文给大家介绍Kotl... 目录一、编程枚举类核心概念二、基础语法与特性1. 基本定义2. 带参数的枚举3. 实现接口4. 内置属性三、

基于Python编写自动化邮件发送程序(进阶版)

《基于Python编写自动化邮件发送程序(进阶版)》在数字化时代,自动化邮件发送功能已成为企业和个人提升工作效率的重要工具,本文将使用Python编写一个简单的自动化邮件发送程序,希望对大家有所帮助... 目录理解SMTP协议基础配置开发环境构建邮件发送函数核心逻辑实现完整发送流程添加附件支持功能实现htm

python连接sqlite3简单用法完整例子

《python连接sqlite3简单用法完整例子》SQLite3是一个内置的Python模块,可以通过Python的标准库轻松地使用,无需进行额外安装和配置,:本文主要介绍python连接sqli... 目录1. 连接到数据库2. 创建游标对象3. 创建表4. 插入数据5. 查询数据6. 更新数据7. 删除

Jenkins的安装与简单配置过程

《Jenkins的安装与简单配置过程》本文简述Jenkins在CentOS7.3上安装流程,包括Java环境配置、RPM包安装、修改JENKINS_HOME路径及权限、启动服务、插件安装与系统管理设置... 目录www.chinasem.cnJenkins安装访问并配置JenkinsJenkins配置邮件通知

Python yield与yield from的简单使用方式

《Pythonyield与yieldfrom的简单使用方式》生成器通过yield定义,可在处理I/O时暂停执行并返回部分结果,待其他任务完成后继续,yieldfrom用于将一个生成器的值传递给另一... 目录python yield与yield from的使用代码结构总结Python yield与yield

Python获取浏览器Cookies的四种方式小结

《Python获取浏览器Cookies的四种方式小结》在进行Web应用程序测试和开发时,获取浏览器Cookies是一项重要任务,本文我们介绍四种用Python获取浏览器Cookies的方式,具有一定的... 目录什么是 Cookie?1.使用Selenium库获取浏览器Cookies2.使用浏览器开发者工具

Java获取当前时间String类型和Date类型方式

《Java获取当前时间String类型和Date类型方式》:本文主要介绍Java获取当前时间String类型和Date类型方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录Java获取当前时间String和Date类型String类型和Date类型输出结果总结Java获取