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

相关文章

Java五子棋之坐标校正

上篇针对了Java项目中的解构思维,在这篇内容中我们不妨从整体项目中拆解拿出一个非常重要的五子棋逻辑实现:坐标校正,我们如何使漫无目的鼠标点击变得有序化和可控化呢? 目录 一、从鼠标监听到获取坐标 1.MouseListener和MouseAdapter 2.mousePressed方法 二、坐标校正的具体实现方法 1.关于fillOval方法 2.坐标获取 3.坐标转换 4.坐

计算绕原点旋转某角度后的点的坐标

问题: A点(x, y)按顺时针旋转 theta 角度后点的坐标为A1点(x1,y1)  ,求x1 y1坐标用(x,y)和 theta 来表示 方法一: 设 OA 向量和x轴的角度为 alpha , 那么顺时针转过 theta后 ,OA1 向量和x轴的角度为 (alpha - theta) 。 使用圆的参数方程来表示点坐标。A的坐标可以表示为: \[\left\{ {\begin{ar

一份LLM资源清单围观技术大佬的日常;手把手教你在美国搭建「百万卡」AI数据中心;为啥大模型做不好简单的数学计算? | ShowMeAI日报

👀日报&周刊合集 | 🎡ShowMeAI官网 | 🧡 点赞关注评论拜托啦! 1. 为啥大模型做不好简单的数学计算?从大模型高考数学成绩不及格说起 司南评测体系 OpenCompass 选取 7 个大模型 (6 个开源模型+ GPT-4o),组织参与了 2024 年高考「新课标I卷」的语文、数学、英语考试,然后由经验丰富的判卷老师评判得分。 结果如上图所

回调的简单理解

之前一直不太明白回调的用法,现在简单的理解下 就按这张slidingmenu来说,主界面为Activity界面,而旁边的菜单为fragment界面。1.现在通过主界面的slidingmenu按钮来点开旁边的菜单功能并且选中”区县“选项(到这里就可以理解为A类调用B类里面的c方法)。2.通过触发“区县”的选项使得主界面跳转到“区县”相关的新闻列表界面中(到这里就可以理解为B类调用A类中的d方法

自制的浏览器主页,可以是最简单的桌面应用,可以把它当成备忘录桌面应用

自制的浏览器主页,可以是最简单的桌面应用,可以把它当成备忘录桌面应用。如果你看不懂,请留言。 完整代码: <!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><ti

Python应用开发——30天学习Streamlit Python包进行APP的构建(9)

st.area_chart 显示区域图。 这是围绕 st.altair_chart 的语法糖。主要区别在于该命令使用数据自身的列和指数来计算图表的 Altair 规格。因此,在许多 "只需绘制此图 "的情况下,该命令更易于使用,但可定制性较差。 如果 st.area_chart 无法正确猜测数据规格,请尝试使用 st.altair_chart 指定所需的图表。 Function signa

python实现最简单循环神经网络(RNNs)

Recurrent Neural Networks(RNNs) 的模型: 上图中红色部分是输入向量。文本、单词、数据都是输入,在网络里都以向量的形式进行表示。 绿色部分是隐藏向量。是加工处理过程。 蓝色部分是输出向量。 python代码表示如下: rnn = RNN()y = rnn.step(x) # x为输入向量,y为输出向量 RNNs神经网络由神经元组成, python

【青龙面板辅助】JD商品自动给好评获取京豆脚本

1.打开链接 开下面的链接进入待评价商品页面 https://club.jd.com/myJdcomments/myJdcomments.action?sort=0 2.登陆后执行脚本 登陆后,按F12键,选择console,复制粘贴以下代码,先运行脚本1,再运行脚本2 脚本1代码 可以自行修改评价内容。 var content = '材质很好,质量也不错,到货也很快物流满分,包装快递满

宝塔面板部署青龙面板教程【简单易上手】

首先,你得有一台部署了宝塔面板的服务器(自己用本地电脑也可以)。 宝塔面板部署自行百度一下,很简单,这里就不走流程了,官网版本就可以,无需开心版。 首先,打开宝塔面板的软件商店,找到下图这个软件(Docker管理器)安装,青龙面板还是安装在docker里,这里依赖宝塔面板安装和管理docker。 安装完成后,进入SSH终端管理,输入代码安装青龙面板。ssh可以直接宝塔里操作,也可以安装ssh连接

BD错误集锦8——在集成Spring MVC + MyBtis编写mapper文件时需要注意格式 You have an error in your SQL syntax

报错的文件 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.yuan.dao.YuanUserDao"><!