如何将OpenWeatherMap五日天气预报API返回的JSON数据拆分为按日分组数组?
Hey there! Let's fix that grouping issue for your 5-day weather forecast. The problem with your current cut() method is it just grabs the first 5 entries—which are all within the first 15 hours of the forecast, not 5 distinct days. Here's how to properly group the 3-hour data into daily chunks and adapt it to your ViewPager setup:
Step 1: Create a Daily Weather Data Class
First, let's define a class to hold daily summary data (since each ViewPager page should represent one full day, not a single 3-hour snapshot):
data class DailyWeather( val date: String, val maxTemp: Double, val minTemp: Double, val weatherIcon: String, val description: String ): java.io.Serializable // Needed for passing via Bundle
Step 2: Replace the cut() Method with Proper Grouping
Update your FiveDaysForecastViewModel to group the 3-hour entries by date, then convert each group into a DailyWeather object. We'll use the dt timestamp (more reliable than string parsing) to get the date:
class FiveDaysForecastViewModel : ViewModel() { // Update LiveData type to use our new DailyWeather class private val _fiveDaysForecast: MutableLiveData<List<DailyWeather>> = MutableLiveData() val fiveDaysForecast: LiveData<List<DailyWeather>> = _fiveDaysForecast @ExperimentalSerializationApi fun getFiveDaysForecast(cityId: Int) { RetrofitClient.api .getFiveDaysForecast(cityId = cityId, unit = "metric", lang = "ru") .enqueue(object : Callback<FiveDaysForecastResult> { override fun onResponse( call: Call<FiveDaysForecastResult>, response: Response<FiveDaysForecastResult> ) { if (response.isSuccessful) { response.body()?.let { val dailyWeatherList = it.list.convertToDailyWeather() _fiveDaysForecast.postValue(dailyWeatherList) Timber.d("Successfully grouped daily weather data") } } else { Timber.d("Failed to fetch forecast: ${response.errorBody()?.string()}") } } override fun onFailure(call: Call<FiveDaysForecastResult>, t: Throwable) { Timber.d("Request failed: ${t.localizedMessage}") } }) } // Extension function to group 3-hour data into daily summaries private fun List<MyList>.convertToDailyWeather(): List<DailyWeather> { val dateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.getDefault()) return this.groupBy { entry -> // Convert timestamp (seconds) to Date, then format to yyyy-MM-dd val date = Date(entry.dt * 1000L) dateFormatter.format(date) } .values // Get all entries grouped by date .take(5) // Keep only the first 5 days .map { dailyEntries -> // Calculate daily stats from all 3-hour entries val allMainData = dailyEntries.map { it.main } val firstWeather = dailyEntries.first().weather.first() DailyWeather( date = dateFormatter.format(Date(dailyEntries.first().dt * 1000L)), maxTemp = allMainData.maxOf { it.temp_max }, minTemp = allMainData.minOf { it.temp_min }, weatherIcon = firstWeather.icon, description = firstWeather.description ) } } }
Step 3: Update the ViewPager Adapter
Modify your PagerAdapter to work with DailyWeather instead of MyList:
class PagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) : FragmentStateAdapter(fragmentManager, lifecycle) { private var data: List<DailyWeather> = listOf() override fun getItemCount() = data.size fun submitData(data: List<DailyWeather>) { this.data = data notifyDataSetChanged() } override fun createFragment(position: Int): Fragment { return if (position in data.indices) { DayWeatherFragment().apply { arguments = Bundle().apply { putSerializable("TAG", data[position]) } } } else Fragment() } }
Step 4: Update the Day Fragment to Display Daily Data
Adjust your DayWeatherFragment to use the DailyWeather data and display meaningful daily info:
class DayWeatherFragment : Fragment() { private lateinit var binding: FragmentDayWeatherBinding override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { binding = FragmentDayWeatherBinding.inflate(inflater) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val dailyWeather: DailyWeather = arguments?.getSerializable("TAG") as DailyWeather // Populate your TextViews with daily summary data binding.txtDate.text = dailyWeather.date binding.txtMaxTemp.text = "Max: ${String.format("%.1f", dailyWeather.maxTemp)}°C" binding.txtMinTemp.text = "Min: ${String.format("%.1f", dailyWeather.minTemp)}°C" binding.txtWeatherDesc.text = dailyWeather.description.capitalize() // Optional: Load weather icon using Glide/Picasso // Glide.with(this) // .load("https://openweathermap.org/img/w/${dailyWeather.weatherIcon}.png") // .into(binding.imgWeatherIcon) } }
Key Notes
- We use
groupBywith a formatted date to cluster all 3-hour entries into daily groups. - For each day, we calculate the max/min temperature from all entries in that day.
- Using the timestamp (
dt) instead of parsingdt_txtmakes the code more robust to any format changes in the API response. - The
DailyWeatherclass is serializable so we can safely pass it between fragments via a Bundle.
Now your ViewPager will display 5 distinct days of weather, each showing a proper daily summary instead of random 3-hour snapshots!
内容的提问来源于stack exchange,提问作者SoulReaver313

