I'm creating a Weather app and i'm trying to display an MPChart which will use the hourly temps as Y points & the Hour (in "HH" format) as the XAxisLabels/ X points.
I'm creating the LineData for the chart in my Fragment's viewModel and expose it to the Fragment via liveData. I also create the XAxisLabels for the chart on my Fragment. The LineData seem to contain the right entries (viewModel: [Entry, x: 0.0 y: 39.07, Entry, x: 1.0 y: 38.68, Entry, x: 2.0 y: 38.16, Entry, x: 3.0 y: 37.29, Entry, x: 4.0 y: 36.2, Entry, x: 5.0 y: 35.22,........]
), but the chart isn't even displaying any of the data. What am I missing here?
Here's what is rendered:
Here's the code:
ViewModel.kt
class WeatherViewModel(
private val forecastRepository: ForecastRepository,
private val weatherUnitConverter: WeatherUnitConverter,
context: Context
) : ViewModel() {
val hourlyWeatherEntries = forecastRepository.getHourlyWeather()
val hourlyChartData = hourlyWeatherEntries.switchMap { hourlyData ->
liveData {
Log.d("ViewModel hourly data", "$hourlyData")
val task = viewModelScope.async(Dispatchers.Default) {
createChartData(hourlyData)
}
emit(task.await())
}
}
/**
* Creates the line chart's data and returns them.
* @return The line chart's data (x,y) value pairs
*/
private fun createChartData(hourlyWeather: List<HourWeatherEntry>?): LineData {
if (hourlyWeather == null) return LineData()
Log.d("ViewModel class", hourlyWeather.toString())
val unitSystemValue = preferences.getString(UNIT_SYSTEM_KEY, "si")!!
val values = arrayListOf<Entry>()
for (i in hourlyWeather.indices) { // init data points
// format the temperature appropriately based on the unit system selected
val hourTempFormatted = when (unitSystemValue) {
UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> hourlyWeather[i].temperature
UnitSystem.US.name.toLowerCase(Locale.ROOT) -> weatherUnitConverter.convertToFahrenheit(
hourlyWeather[i].temperature
)
else -> hourlyWeather[i].temperature
}
// Create the data point
values.add(
Entry(
i.toFloat(),
hourTempFormatted.toFloat(),
appContext.resources.getDrawable(
determineSummaryIcon(hourlyWeather[i].icon),
null
)
)
)
}
Log.d("MainFragment viewModel", "$values")
// create a data set and customize it
val lineDataSet = LineDataSet(values, "")
val color = appContext.resources.getColor(R.color.black, null)
val offset = MPPointF.getInstance()
offset.y = -35f
lineDataSet.apply {
valueFormatter = YValueFormatter()
setDrawValues(true)
fillDrawable = appContext.resources.getDrawable(R.drawable.gradient_night_chart, null)
setDrawFilled(true)
setDrawIcons(true)
setCircleColor(color)
mode = LineDataSet.Mode.HORIZONTAL_BEZIER
this.color = color // line color
iconsOffset = offset
lineWidth = 3f
valueTextSize = 9f
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
valueTypeface = appContext.resources.getFont(R.font.work_sans_medium)
}
}
// create a LineData object using our LineDataSet
val data = LineData(lineDataSet)
data.apply {
setValueTextColor(R.color.colorPrimary)
setValueTextSize(15f)
}
return data
}
}
MainFragment.kt
class MainFragment : Fragment() {
// Lazy inject the view model
private val viewModel: WeatherViewModel by viewModel()
private lateinit var hourlyChart: LineChart
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setUpChart()
lifecycleScope.launch {
// Shows a lottie animation while the data is being loaded
//scrollView.visibility = View.GONE
//lottieAnimView.visibility = View.VISIBLE
bindUIAsync().await()
// Stops the animation and reveals the layout with the data loaded
//scrollView.visibility = View.VISIBLE
//lottieAnimView.visibility = View.GONE
}
}
@SuppressLint("SimpleDateFormat")
private fun bindUIAsync() = lifecycleScope.async(Dispatchers.Main) {
// fetch hourly chart line data
val hourlyChartData = viewModel.hourlyChartData
hourlyChartData.observe(viewLifecycleOwner, Observer { lineData ->
if(lineData == null) {
Log.d("HOURLY CHART DATA", "Line data = null")
return@Observer
}
hourlyChart.data = lineData
hourlyChart.invalidate()
})
// fetch hourly weather
val hourlyWeather = viewModel.hourlyWeatherEntries//viewModel.hourlyWeatherEntries
// Observe the hourly weather live data
hourlyWeather.observe(viewLifecycleOwner, Observer { hourly ->
if (hourly == null) return@Observer
val xAxisLabels = arrayListOf<String>()
val sdf = SimpleDateFormat("HH")
for (i in hourly.indices) {
val formattedLabel = sdf.format(Date(hourly[i].time * 1000))
xAxisLabels.add(formattedLabel)
}
setChartAxisLabels(xAxisLabels)
Log.d(TAG, "label count = ${hourlyChart.xAxis.labelCount.toString()}")
})
return@async true
}
private fun setChartAxisLabels(labels: ArrayList<String>) {
// Populate the X axis with the hour labels
hourlyChart.xAxis.valueFormatter = IndexAxisValueFormatter(labels)
}
/**
* Sets up the chart with the appropriate
* customizations.
*/
private fun setUpChart() {
hourlyChart.apply {
description.isEnabled = false
setNoDataText("Data is loading...")
// enable touch gestures
setTouchEnabled(true)
dragDecelerationFrictionCoef = 0.9f
// enable dragging
isDragEnabled = true
isHighlightPerDragEnabled = true
setDrawGridBackground(false)
axisRight.setDrawLabels(false)
axisLeft.setDrawLabels(false)
axisLeft.setDrawGridLines(false)
// disable zoom functionality
setScaleEnabled(false)
setPinchZoom(false)
isDoubleTapToZoomEnabled = false
// disable the chart's legend
legend.isEnabled = false
// append extra offsets to the chart's auto-calculated ones
setExtraOffsets(0f, 0f, 0f, 10f)
data = LineData()
data.isHighlightEnabled = false
setVisibleXRangeMaximum(6f)
setBackgroundColor(resources.getColor(R.color.bright_White, null))
}
// X Axis setup
hourlyChart.xAxis.apply {
position = XAxis.XAxisPosition.BOTTOM
textSize = 14f
setDrawLabels(true)
setDrawAxisLine(false)
setDrawGridLines(false)
isEnabled = true
granularity = 1f // one hour
spaceMax = 0.2f // add padding start
spaceMin = 0.2f // add padding end
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
typeface = resources.getFont(R.font.work_sans)
}
textColor = resources.getColor(R.color.black, null)
}
// Left Y axis setup
hourlyChart.axisLeft.apply {
setDrawLabels(true)
setDrawGridLines(false)
setPosition(YAxis.YAxisLabelPosition.OUTSIDE_CHART)
// temperature values range (higher than probable temps in order to scale down the chart)
axisMinimum = 0f
axisMaximum = when (getUnitSystemValue()) {
UnitSystem.SI.name.toLowerCase(Locale.ROOT) -> 50f
UnitSystem.US.name.toLowerCase(Locale.ROOT) -> 150f
else -> 50f
}
}
// Right Y axis setup
hourlyChart.axisRight.apply {
setDrawGridLines(false)
isEnabled = false
}
}
}
from MPChart won't display hourly weather data
No comments:
Post a Comment