kotlin 编写一个简单的天气预报app(五)增加forcast接口并显示

参考资料

OpenWeatherMap提供了一个/forecast接口,用于获取未来几天的天气预报。你可以使用HTTP GET请求访问该接口,并根据你所在的城市或地理坐标获取相应的天气数据。

以下是一个示例请求的URL和一些常用的参数:

URL: http://api.openweathermap.org/data/2.5/forecast

查询参数:

  • q (必需): 城市名称 (e.g. "London,uk") 或城市ID (可在OpenWeatherMap网站上获得) 或地理坐标 (使用纬度和经度, e.g. "37.7749,-122.4194")。
  • appid (必需): 你的OpenWeatherMap API密钥。
    可选参数:
    units: 温度单位 (例如 "metric" 表示摄氏度, "imperial" 表示华氏度)。
    lang: 返回的天气描述语言 (例如 "en" 表示英语)。

从openWeatherMap获取forecast

1.在WeatherService接口中增加请求函数。

getForecastByCityName:此方法与 getWeatherByCityName 方法类似,但它检索预报数据而不是当前天气数据。它还采用城市名称和 API 密钥作为参数,并返回 ForecastResponse 类型的 Call 对象,这是从 API 收到的响应。

kotlin 复制代码
interface WeatherService {

    @GET("weather")
    fun getWeatherByCityName(
        @Query("q") cityName : String,
        @Query("appid") apiKey : String
    ) : Call<WeatherResponse>

    @GET("forecast")
    fun getForecastByCityName(
        @Query("q") cityName : String,
        @Query("appid") apiKey : String
    ) : Call<ForecastResponse>
}

2.编译一个新的ForecastResponse 类,用于解析天气预报的 JSON 数据。它具有以下属性:

  • cod:表示响应 JSON 中的 cod 值的字符串变量。
  • message:表示响应 JSON 中的消息值的整数变量。
  • cnt:表示响应 JSON 中的 cnt 值的整数变量。
  • forecastCellList:ForecastCell 对象的ArrayList,表示响应JSON 中的预测单元格列表。
  • forecastCity:ForecastCity 对象,表示响应 JSON 中的城市详细信息。
    这些属性使用 @SerializedName 进行注释,以指定 JSON 中相应的键。提供默认值是为了初始化目的。
kotlin 复制代码
package com.example.myweather.openWeatherMap

import com.example.myweather.WeatherResponseClouds
import com.example.myweather.WeatherResponseCoord
import com.example.myweather.WeatherResponseWeather
import com.google.gson.annotations.SerializedName

data class ForecastResponse (
    @SerializedName("cod")
    var cod: String = "",
    @SerializedName("message")
    var message: Int = 0,
    @SerializedName("cnt")
    var cnt : Int = 0,
    @SerializedName("list")
    var forecastCellList : ArrayList<ForecastCell>? = null,
    @SerializedName("city")
    var forecastCity: ForecastCity? = null
)


data class ForecastCell (
    @SerializedName("dt")
    val dt: Long,
    @SerializedName("main")
    val main: ForecastMain,
    @SerializedName("weather")
    val weather: List<WeatherResponseWeather>,
    @SerializedName("clouds")
    val clouds: WeatherResponseClouds,
    @SerializedName("wind")
    val wind: ForecastWind,
    @SerializedName("visibility")
    val visibility: Int = 0,
    @SerializedName("pop")
    val pop: Double = 0.0,
    @SerializedName("rain")
    val rain: ForecastRain,
    @SerializedName("snow")
    val snow: ForecastSnow,
    @SerializedName("sys")
    val sys: ForecastSys,
    @SerializedName("dt_txt")
    val dt_txt: String = ""
)

data class ForecastCity(
    @SerializedName("id")
    val id: Int = 0,
    @SerializedName("name")
    val name: String = "",
    @SerializedName("coord")
    val coord: WeatherResponseCoord,
    @SerializedName("country")
    val country: String ="",
    @SerializedName("population")
    val population:Int = 0,
    @SerializedName("timezone")
    val timezone: Int = 0,
    @SerializedName("sunrise")
    val sunrise: Int = 0,
    @SerializedName("sunset")
    val sunset: Int = 0
)

data class ForecastMain(
    @SerializedName("temp")
    val temperature: Double = 0.0,
    @SerializedName("feels_like")
    val feelsLike: Double = 0.0,
    @SerializedName("temp_min")
    val minTemperature: Double = 0.0,
    @SerializedName("temp_max")
    val maxTemperature: Double = 0.0,
    @SerializedName("pressure")
    val pressure: Int = 0,
    @SerializedName("sea_level")
    val seaLevel: Int = 0,
    @SerializedName("grnd_level")
    val groundLevel: Int = 0,
    @SerializedName("humidity")
    val humidity: Int = 0,
    @SerializedName("temp_kf")
    val temperatureKf: Double = 0.0
)

data class ForecastWind(
    @SerializedName("speed")
    val speed: Double = 0.0,
    @SerializedName("deg")
    val degree: Int = 0,
    @SerializedName("gust")
    val gust : Double = 0.0
)

data class ForecastRain(
    @SerializedName("3h")
    val heightInThreeHours: Double = 0.0
)

data class ForecastSnow(
    @SerializedName("3h")
    val heightInThreeHours: Double = 0.0
)

data class ForecastSys(
    @SerializedName("pod")
    val partOfDay: String = ""
)

3.在CustomEvent.kt中增加ForecastResponseEvent事件

kotlin 复制代码
class ForecastResponseEvent(val forecastResponse: ForecastResponse)

4.在RetrofitClient.kt中增加getForecastByCityName函数,用来MainActivity中调用请求接口:

kotlin 复制代码
    fun getForecastByCityName(cityName: String) {
        val call = weatherService.getForecastByCityName(cityName, API_KEY)
        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!!)
            }
        })

5.并且增加了相应函数

  • handleForecastFailure接受消息字符串作为参数并将其与前缀一起打印出来。
  • handleForecastData接受一个ForecastResponse对象作为参数。它检查该对象是否为空,如果不为空,则创建一个对象ForecastResponseEvent并使用 EventBus 发布它。然后它调用该printForecastResponse函数并传入该ForecastResponse对象。
  • printForecastResponse接受一个ForecastResponse对象作为参数,并打印出该对象的各种属性,例如 、cod、message和cnt的大小forecastCellList。它还打印出对象的id和属性。nameforecastCity
kotlin 复制代码
    private fun handleForecastFailure(message: String) {
        println("handleForecastFailure:${message}")
    }

    private fun handleForecastData(forecastData: ForecastResponse?) {
        if(forecastData == null) return

        val forecastResponseEvent = ForecastResponseEvent(forecastData)
        EventBus.getDefault().post(forecastResponseEvent)		//这里发送了forecastResponseEvent

        printForecastResponse(forecastData)
    }

    private  fun printForecastResponse(forecastResponse: ForecastResponse) {
        println("cod:${forecastResponse.cod}")
        println("message:${forecastResponse.message}")
        println("cnt:${forecastResponse.cnt}")
        println("list:${forecastResponse.forecastCellList?.size}")
        println("city id:${forecastResponse.forecastCity?.id} name:${forecastResponse.forecastCity?.name}")
    }

6.在MainActivity中,处理forecastResponseEvent事件:

该函数是一个事件处理程序,在收到onReceiveForecastResponsea 时调用。ForecastResponseEvent它采用事件对象作为参数,其中包含预测响应数据。该函数调用该updateForecastList函数根据接收到的数据更新预测列表。

updateForecastList函数接受一个ForecastResponse对象作为参数。然后,它创建一个SimpleDateFormat对象来格式化预测响应中的日期和时间。该函数初始化一个空的可变列表data来存储格式化的预测数据。

然后,该函数会迭代 的预测单元格列表中的每个单元格forecastResponse。对于每个单元格,它都会创建一个字符串,oneLine其中包含格式化的日期和时间、温度、feel_like、天气主体和天气描述。通过减去常数值并将其转换为整数,将温度从开尔文转换为摄氏度kelvins。

每个oneLine字符串都会添加到data列表中。

最后,该函数创建一个ArrayAdapter以data列表为数据源的适配器,并将其设置为ListViewID 的适配器listViewTodayForcast。这将使用更新的预测数据更新列表视图。

kotlin 复制代码
    @RequiresApi(Build.VERSION_CODES.O)
    @Subscribe(threadMode = ThreadMode.MAIN)
    fun onReceiveForecastResponse(event: ForecastResponseEvent) {
        updateForecastList(event.forecastResponse)
    }
    
    private fun updateForecastList(forecastResponse: ForecastResponse) {
        val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.ENGLISH)
        val data = mutableListOf<String>()
        for (cell in forecastResponse.forecastCellList!!) {
            val oneLine = "${simpleDateFormat.format(cell.dt*1000L)}\n" +
                    "temperature:${cell.main.temperature.minus(kelvins).toInt()}," +
                    "feel_like:${cell.main.feelsLike.minus(kelvins).toInt()},\n" +
                    "weather:${cell.weather.first().main},${cell.weather.first().description}"
            data.add(oneLine)
        }

        val adapter = ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, data)
        findViewById<ListView>(R.id.listViewTodayForcast).adapter = adapter
    }

7.我在主界面中增加了一个ListView用来显示forecast返回的数据

xml 复制代码
    <ListView
        android:id="@+id/listViewTodayForcast"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@id/textViewWeather"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"/>

8.最后的结果:

相关推荐
懒大王爱吃狼36 分钟前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
秃头佛爷2 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
待磨的钝刨2 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
XiaoLeisj4 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师5 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉5 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer5 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java7 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山7 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js