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

相关文章

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

redis群集简单部署过程

《redis群集简单部署过程》文章介绍了Redis,一个高性能的键值存储系统,其支持多种数据结构和命令,它还讨论了Redis的服务器端架构、数据存储和获取、协议和命令、高可用性方案、缓存机制以及监控和... 目录Redis介绍1. 基本概念2. 服务器端3. 存储和获取数据4. 协议和命令5. 高可用性6.

如何利用Java获取当天的开始和结束时间

《如何利用Java获取当天的开始和结束时间》:本文主要介绍如何使用Java8的LocalDate和LocalDateTime类获取指定日期的开始和结束时间,展示了如何通过这些类进行日期和时间的处... 目录前言1. Java日期时间API概述2. 获取当天的开始和结束时间代码解析运行结果3. 总结前言在J

java获取图片的大小、宽度、高度方式

《java获取图片的大小、宽度、高度方式》文章介绍了如何将File对象转换为MultipartFile对象的过程,并分享了个人经验,希望能为读者提供参考... 目China编程录Java获取图片的大小、宽度、高度File对象(该对象里面是图片)MultipartFile对象(该对象里面是图片)总结java获取图片

Java通过反射获取方法参数名的方式小结

《Java通过反射获取方法参数名的方式小结》这篇文章主要为大家详细介绍了Java如何通过反射获取方法参数名的方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1、前言2、解决方式方式2.1: 添加编译参数配置 -parameters方式2.2: 使用Spring的内部工具类 -

Java如何获取视频文件的视频时长

《Java如何获取视频文件的视频时长》文章介绍了如何使用Java获取视频文件的视频时长,包括导入maven依赖和代码案例,同时,也讨论了在运行过程中遇到的SLF4J加载问题,并给出了解决方案... 目录Java获取视频文件的视频时长1、导入maven依赖2、代码案例3、SLF4J: Failed to lo

JAVA调用Deepseek的api完成基本对话简单代码示例

《JAVA调用Deepseek的api完成基本对话简单代码示例》:本文主要介绍JAVA调用Deepseek的api完成基本对话的相关资料,文中详细讲解了如何获取DeepSeekAPI密钥、添加H... 获取API密钥首先,从DeepSeek平台获取API密钥,用于身份验证。添加HTTP客户端依赖使用Jav

使用Java实现获取客户端IP地址

《使用Java实现获取客户端IP地址》这篇文章主要为大家详细介绍了如何使用Java实现获取客户端IP地址,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 首先是获取 IP,直接上代码import org.springframework.web.context.request.Requ

C++实现获取本机MAC地址与IP地址

《C++实现获取本机MAC地址与IP地址》这篇文章主要为大家详细介绍了C++实现获取本机MAC地址与IP地址的两种方式,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实际工作中,项目上常常需要获取本机的IP地址和MAC地址,在此使用两种方案获取1.MFC中获取IP和MAC地址获取