'How to get openweathermap icon from OneCall API nested-list?

I'm adding weather forecast data to my app. The info displays in a RecyclerView after it is received by an API call using Retrofit 2. I'm having trouble getting the icon string id.

Here is the JSON response.

{"lat": 33.44, "lon": -94.04, "timezone": "America/Chicago", "timezone_offset": -21600, "daily": [ { "dt": 1618308000, "sunrise": 1618282134, "sunset": 1618333901, "moonrise": 1618284960, "moonset": 1618339740, "moon_phase": 0.04, "temp": { "day": 279.79, "min": 275.09, "max": 284.07, "night": 275.09, "eve": 279.21, "morn": 278.49 }, "feels_like": { "day": 277.59, "night": 276.27, "eve": 276.49, "morn": 276.27 }, "pressure": 1020, "humidity": 81, "dew_point": 276.77, "wind_speed": 3.06, "wind_deg": 294, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" } ], "clouds": 56, "pop": 0.2, "rain": 0.62, "uvi": 1.93 }]}

I'm making the call and getting the callback. I'm getting the temp and the other info I need for the forecast data. I'm not sure how I can access the icon id in the nested list. Here is how I'm getting the response.

private fun loadWeather() {

    loadWeatherData()
    Log.d("weatherCall", "loadWeather ran")

    rvWeather?.layoutManager = LinearLayoutManager(this)
    rvWeather?.adapter = weatherAdapter


}


private fun loadWeatherData() {
    
    val un = "imperial"
    val wKey = "key"
    val lastUserLatLng = latLng

    val locationLatLng = lastUserLatLng.split(", ").toTypedArray()
    val locationLatitude = locationLatLng[0]
    val locationLongitude = locationLatLng[1]

    val baseURL = "https://api.openweathermap.org/"

    Log.d("weatherCall", "baseURL: $baseURL")
    val retro = Retrofit.Builder()
        .baseUrl(baseURL)
        .addConverterFactory(GsonConverterFactory.create())
        .build()

    val api = retro.create(ApiCallService::class.java)

    api.fetchAllData(locationLatitude, locationLongitude, un, wKey).enqueue(object : Callback<WeatherResponse> {
        override fun onResponse(
            call: Call<WeatherResponse>,
            response: Response<WeatherResponse>
        ) {
            if(response.isSuccessful) {


                val da = response.body()!!.daily

                weatherAdapter.dailyList = da
                weatherAdapter.notifyDataSetChanged()
            }
        }

        override fun onFailure(call: Call<WeatherResponse>, t: Throwable) {
            Log.d("weatherCall", "fetch onFail ran response: $t")
            rvWeather?.gone()
        }

    })

}

I save the ArrayList to the List for the RecyclerviewAdapter.

Here is the interface:

interface ApiCallService {

@GET("data/2.5/onecall?")
fun fetchAllData(@Query("lat") lat: String, @Query("lon") lon: String, @Query("units") units: String, @Query("appid") appId: String): Call<WeatherResponse>

}

I'm using the OneCall API which brings back all the info I need in just the one call. the response is like this:

data class WeatherResponse(
val lat: Number,
val lon: Number,
val timezone: String,
val timezone_offset: Number,
val current: CurrentList,
val minutely: ArrayList<Minutely>,
val hourly: ArrayList<Hourly>,
val daily: ArrayList<DailyList>,
val alerts: ArrayList<AlertsList>
)

I get the temperature from the TempList and I need to access the weather: ArrayList to grab the weather icon id, here is the DailyList

data class DailyList(
val dt: Number,
val moon_phase: Number,
val moonrise: Number,
val moonset: Number,
val sunrise: Number,
val sunset: Number,
val temp: TempList,
val feels_like: FeelsList,
val pressure: Number,
val humidity: Number,
val dew_point: Number,
val wind_speed: Number,
val wind_deg: Number,
val weather: ArrayList<DailyWeatherList>,
val clouds: Number,
val pop: Number,
val rain: Number,
val snow: Number,
val uvi: Number)

and here is the ArrayList:

data class DailyWeatherList(
val description: String,
val icon: String,
val id: Number,
val main: String)

Here is my rv adapter class:

class WeatherForecastAdapter(var dailyList: List<DailyList>): RecyclerView.Adapter<RecyclerView.ViewHolder>() {

override fun getItemCount(): Int {

    return dailyList.size

}

class WeatherForecastViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
    fun bind(postModel: DailyList) {

        val tvDayOfWeek = itemView.findViewById<TextView>(R.id.tvDayOfWeek)
        val tvDateOfMonth = itemView.findViewById<TextView>(R.id.tvDateOfMonth)
        val tvTemp = itemView.findViewById<TextView>(R.id.tvTemp)
        val ivWeatherIcon = itemView.findViewById<ImageView>(R.id.ivWeatherIcon)

        val iconURL = postModel.weather[position].icon 
        val tem = postModel.temp.max // I originally tried this same code block here in the fun bind, but I couldnt get the position, always a crash. I used absoluteAdapterPosition, and bindingAdapterPosition but they both crash the app
        val userImage = "http://openweathermap.org/img/wn/[email protected]"

        tvTemp.text = tem.toString()
        Glide.with(itemView).load(userImage).into(ivWeatherIcon)



    }
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {

    Log.d("weatherCall", "onCreateViewHolder ran")

    val view = LayoutInflater.from(parent.context)
        .inflate(R.layout.weather_forecast_card, parent, false)
    return WeatherForecastViewHolder(view)

}

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {

    (holder as WeatherForecastViewHolder).bind(dailyList[position])

    -- moving the code to the onBineViewHolder I thought I would be able use the position here but no luck.

    val tvDayOfWeek = holder.itemView.findViewById<TextView>(R.id.tvDayOfWeek)
    val tvDateOfMonth = holder.itemView.findViewById<TextView>(R.id.tvDateOfMonth)
    val tvTemp = holder.itemView.findViewById<TextView>(R.id.tvTemp)
    val ivWeatherIcon = holder.itemView.findViewById<ImageView>(R.id.ivWeatherIcon)

    val iconURL = dailyList[position]
    iconURL.weather[position].icon
    val tem = dailyList[position].temp.max
    val userImage = "http://openweathermap.org/img/wn/[email protected]"

    tvTemp.text = tem.toString()
    Glide.with(holder.itemView).load(userImage).into(ivWeatherIcon)

}}

The position works for the primary list, but not for the nested list. This works fine for the temp:

val tem = dailyList[position].temp.max

but this doesnt work to get the "icon":

val iconURL = dailyList[position]
    iconURL.weather[position].icon

How can I access the inner ArrayList with the correct position? Is there a better way of saving the main list so that I can access the nested-lists?



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source