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 收到的响应。

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.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事件

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) 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。这将使用更新的预测数据更新列表视图。

    @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返回的数据

    <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.最后的结果:

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,下面是使用 Kotlin 和 SharedPreferences 编写一个简单的注册应用程序的基本步骤: 1. 创建一个新的 Kotlin Android 项目,并在 app 的 build.gradle 文件中添加以下依赖项: ```groovy implementation "androidx.appcompat:appcompat:1.3.0" implementation "androidx.core:core-ktx:1.6.0" ``` 2. 在 res/layout 目录中创建一个名为 activity_register.xml 的新布局文件,并在其中添加以下控件: ```xml <EditText android:id="@+id/usernameEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Username" android:inputType="text" /> <EditText android:id="@+id/passwordEditText" android:layout_width="match_parent" android:layout_height="wrap_content" android:hint="Password" android:inputType="textPassword" /> <Button android:id="@+id/registerButton" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Register" /> ``` 3. 创建一个新的 Kotlin 类,用于表示用户信息。例如: ```kotlin data class User(val username: String, val password: String) ``` 4. 在 MainActivity 类中,实例化 EditText 和 Button 控件,并在 Button 的点击事件中保存用户信息到 SharedPreferences。例如: ```kotlin class MainActivity : AppCompatActivity() { private lateinit var usernameEditText: EditText private lateinit var passwordEditText: EditText private lateinit var registerButton: Button override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_register) usernameEditText = findViewById(R.id.usernameEditText) passwordEditText = findViewById(R.id.passwordEditText) registerButton = findViewById(R.id.registerButton) registerButton.setOnClickListener { val username = usernameEditText.text.toString() val password = passwordEditText.text.toString() val user = User(username, password) saveUser(user) Toast.makeText(this, "Registration successful", Toast.LENGTH_SHORT).show() } } private fun saveUser(user: User) { val sharedPreferences = getSharedPreferences("users", Context.MODE_PRIVATE) val editor = sharedPreferences.edit() editor.putString(user.username, user.password) editor.apply() } } ``` 5. 运行应用程序并测试。 这样,一个简单的注册应用程序就完成了。当用户在 EditText 中输入用户名和密码,并单击 Register 按钮时,应用程序将使用 SharedPreferences 保存用户信息。您可以使用 SharedPreferences 的 getString() 方法检索用户密码,以验证用户身份。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

习惯就好zz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值