参考资料
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 收到的响应。
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 中相应的键。提供默认值是为了初始化目的。
package com.example.myweather.openWeatherMapimport com.example.myweather.WeatherResponseClouds
import com.example.myweather.WeatherResponseCoord
import com.example.myweather.WeatherResponseWeather
import com.google.gson.annotations.SerializedNamedata 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事件
class ForecastResponseEvent(val forecastResponse: ForecastResponse)
4.在RetrofitClient.kt中增加getForecastByCityName函数,用来MainActivity中调用请求接口:
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
private fun handleForecastFailure(message: String) {println("handleForecastFailure:${message}")}private fun handleForecastData(forecastData: ForecastResponse?) {if(forecastData == null) returnval forecastResponseEvent = ForecastResponseEvent(forecastData)EventBus.getDefault().post(forecastResponseEvent) //这里发送了forecastResponseEventprintForecastResponse(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。这将使用更新的预测数据更新列表视图。
@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返回的数据
<ListViewandroid: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"/>