0

I'm making weather app using 5 days openweathermap api. My problem is that after request i'm getting big array with 40 weather results for 5 days every 3 hours. I need to make my output show 5 days in view pager for each day of this 5 days so i need to split this json result to array so i can take only what i need. My view pager adapter:

class PagerAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
    FragmentStateAdapter(fragmentManager, lifecycle) {
    private var data: List<MyList> = listOf()

    override fun getItemCount() = data.size

    fun submitData(data: List<MyList>) {
        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()
    }
}

Fragment in view pager. I will fill all text views here:

class DayWeatherFragment : Fragment() {

    private lateinit var binding: FragmentDayWeatherBinding

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentDayWeatherBinding.inflate(inflater)
        // Inflate the layout for this fragment
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val obj: MyList = arguments?.getSerializable("TAG") as MyList

        binding.txtMorningTemp.text = obj.main.temp.toString()
    }

}

This is the fragment where my view pager places:

class FiveDaysForecastFragment : Fragment() {
    private lateinit var binding: FragmentFiveDaysForecastBinding
    private val viewModel: FiveDaysForecastViewModel by viewModels()
    private val adapter by lazy(LazyThreadSafetyMode.NONE) { PagerAdapter(childFragmentManager, lifecycle) }

    @ExperimentalSerializationApi
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentFiveDaysForecastBinding.inflate(inflater)
        binding.viewPager.adapter = adapter

        (activity as AppCompatActivity).setSupportActionBar(binding.toolbar)

        val cityId = this.arguments?.getInt(CITY_ID) ?: 0
        viewModel.getFiveDaysForecast(cityId)

        return binding.root
    }

    @ExperimentalSerializationApi
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        TabLayoutMediator(binding.tabs, binding.viewPager) { tab, position ->
            tab.text = "${position+1}"
        }.attach()

        viewModel.fiveDaysForecast.observe(viewLifecycleOwner) {
            adapter.submitData(it)
        }

        binding.toolbar.setNavigationOnClickListener {
            parentFragmentManager.popBackStack()
        }
    }

}

And view model for this fragment:

class FiveDaysForecastViewModel : ViewModel() {

    private val _fiveDaysForecast: MutableLiveData<List<MyList>> = MutableLiveData()
    val fiveDaysForecast: LiveData<List<MyList>> = _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 list = it.list.cut()
                            _fiveDaysForecast.postValue(list)
                            Timber.d("Searching for: $it")
                        }
                    } else {
                        Timber.d("Can't get city")
                    }
                }

                override fun onFailure(call: Call<FiveDaysForecastResult>, t: Throwable) {
                    Timber.d("Failure request")
                }

            })
    }

    private fun List<MyList>.cut() = if (this.size > 5) this.subList(0, 5) else this
}

WeatheForecastResult:

@Serializable
data class FiveDaysForecastResult(
    val cod: String,
    val message: Int,
    val cnt: Int,
    val list: List<MyList>,
    val city: City
)

MyList:

@Serializable
data class MyList(
    val dt: Int,
    val main: Main,
    val weather: List<Weather>,
    val clouds: Clouds,
    val wind: Wind,
    val visibility: Int,
    val pop: Double,
    val sys: Sys,
    val dt_txt: String,
): java.io.Serializable

enter image description here

10
  • Can you share the code for FiveDaysForecastResult Commented Sep 9, 2021 at 7:50
  • @gioravered yeah. i will add it to the question. Commented Sep 9, 2021 at 7:52
  • @gioravered just added Commented Sep 9, 2021 at 7:53
  • I see the you already use lists to hold the forecast data so the data is not JSON anymore. What exactly you need help with? The actual split to 5 days? Commented Sep 9, 2021 at 7:58
  • @gioravered yeah. I need to split it to 5 days. So i can take weather for first day for example: morning weather (9:00), day (15:00) evening (18:00) and night (00:00) or something like that. And i need to do it for 5 days so that's why i need to split it. Commented Sep 9, 2021 at 8:04

1 Answer 1

1

I would suggest to use a map that uses dates as keys and a list of Weather object as values. This way each date will have its weather entries and you can handle it from there.

A change in your LiveData type is required:

private val _fiveDaysForecast: MutableLiveData<Map<LocalDateTime, List<MyList>>> = MutableLiveData()
val fiveDaysForecast: LiveData<Map<LocalDateTime, List<MyList>>> = _fiveDaysForecast

And in your response parsing:

response.body()?.let {
    val weatherByDate = mutableMapOf<LocalDateTime, MutableList<Weather>>()
    it.list.forEach { weatherItem ->
        val date = LocalDateTime.ofEpochSecond(weatherItem.dt, 0, ZoneOffset.ofTotalSeconds(it.city.timezone))
        weatherByDate.getOrPut(date, { mutableListOf() }).add(weatherItem)
    }

    _fiveDaysForecast.postValue(weatherByDate)

Now you have a map that have a weather entries for each date. Use it to get the 5 days you need.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.