参考资料
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"/>