Skip to content

Commit

Permalink
feat: add Pie and Combined(bar + line) chart in HealthFragment
Browse files Browse the repository at this point in the history
- Add menu(in toolbar) in HealthFragment for changing chart type
- Add Pie chart for KCal, Time and Distance in HealthFragment
- Add Combined chart for KCal, Time and Distance in HealthFragment
  • Loading branch information
princeraj-pr authored and PratyushSingh07 committed Dec 25, 2023
1 parent 3bf7051 commit 2ad6b2b
Show file tree
Hide file tree
Showing 22 changed files with 1,248 additions and 160 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.cyclofit.ui.adapter

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import com.example.cyclofit.ui.fragment.DistanceCombineChartFragment
import com.example.cyclofit.ui.fragment.KcalCombinedChartFragment
import com.example.cyclofit.ui.fragment.TimeCombinedChartFragment

class CombinedChartAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
LineChartAdapter(fragmentManager, lifecycle) {

override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> KcalCombinedChartFragment()
1 -> TimeCombinedChartFragment()
else -> DistanceCombineChartFragment()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,17 @@ import com.example.cyclofit.ui.fragment.DistanceFragment
import com.example.cyclofit.ui.fragment.KcalFragment
import com.example.cyclofit.ui.fragment.TimeFragment

class ViewAdapter(fragmentManager: FragmentManager,lifecycle : Lifecycle) : FragmentStateAdapter(fragmentManager,lifecycle) {
open class LineChartAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
FragmentStateAdapter(fragmentManager, lifecycle) {
override fun getItemCount(): Int {
return 3
}

override fun createFragment(position: Int): Fragment {
when(position){
0 -> return KcalFragment()
1 -> return TimeFragment()
else -> return DistanceFragment()
return when (position) {
0 -> KcalFragment()
1 -> TimeFragment()
else -> DistanceFragment()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.example.cyclofit.ui.adapter

import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle
import com.example.cyclofit.ui.fragment.DistancePieChartFragment
import com.example.cyclofit.ui.fragment.KcalPeiChartFragment
import com.example.cyclofit.ui.fragment.TimePieCharFragment

class PieChartAdapter(fragmentManager: FragmentManager, lifecycle: Lifecycle) :
LineChartAdapter(fragmentManager, lifecycle) {

override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> KcalPeiChartFragment()
1 -> TimePieCharFragment()
else -> DistancePieChartFragment()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
package com.example.cyclofit.ui.fragment

import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.res.ResourcesCompat
import androidx.fragment.app.Fragment
import com.example.cyclofit.R
import com.example.cyclofit.databinding.FragmentDistanceCombineChartBinding
import com.example.cyclofit.model.Shared
import com.example.cyclofit.ui.utils.Constants
import com.example.cyclofit.ui.utils.Months
import com.github.mikephil.charting.charts.CombinedChart
import com.github.mikephil.charting.components.AxisBase
import com.github.mikephil.charting.components.Legend
import com.github.mikephil.charting.components.XAxis
import com.github.mikephil.charting.components.YAxis
import com.github.mikephil.charting.data.BarData
import com.github.mikephil.charting.data.BarDataSet
import com.github.mikephil.charting.data.BarEntry
import com.github.mikephil.charting.data.CombinedData
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.LineData
import com.github.mikephil.charting.data.LineDataSet
import com.github.mikephil.charting.formatter.ValueFormatter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type

class DistanceCombineChartFragment : Fragment() {
lateinit var binding: FragmentDistanceCombineChartBinding
var sp = ArrayList<Shared>()

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentDistanceCombineChartBinding.inflate(inflater, container, false)
val sharedPreferences1 = requireContext().getSharedPreferences(
Constants.CYCLOFIT_PREFERENCES,
Context.MODE_PRIVATE
)
val gson = Gson()
val json = sharedPreferences1.getString("timer", null)
val type: Type = object : TypeToken<ArrayList<Shared?>?>() {}.type

if (json != null) {
sp = gson.fromJson(json, type)
}

val distanceArrayList = ArrayList<String>()
for (i in 0 until sp.size) {
distanceArrayList.add("0")
}
val lineDataList = mutableListOf<Entry>()
val barDataList = mutableListOf<BarEntry>()
for ((x, i) in distanceArrayList.withIndex()) {
lineDataList.add(Entry(x.toFloat(), i.toFloat()))
barDataList.add(BarEntry(x.toFloat(), i.toFloat()))
}
val combinedData = CombinedData().apply {
setData(generateLineData(lineDataList))
setData(generateBarData(barDataList))
setValueTypeface(Typeface.DEFAULT)
}
setCombinedChart(combinedData)
return binding.root
}

private fun setCombinedChart(combinedData: CombinedData) {
binding.combinedChart.apply {
data = combinedData
invalidate()
description.isEnabled = false
setBackgroundColor(Color.TRANSPARENT)
setDrawGridBackground(false)
setDrawBarShadow(false)
isHighlightFullBarEnabled = false
// draw bars behind lines
drawOrder = arrayOf(
CombinedChart.DrawOrder.BAR,
CombinedChart.DrawOrder.BUBBLE,
CombinedChart.DrawOrder.CANDLE,
CombinedChart.DrawOrder.LINE,
CombinedChart.DrawOrder.SCATTER
)
legend.apply {
isWordWrapEnabled = true
verticalAlignment = Legend.LegendVerticalAlignment.BOTTOM
horizontalAlignment = Legend.LegendHorizontalAlignment.CENTER
orientation = Legend.LegendOrientation.HORIZONTAL
setDrawInside(false)
}
customizeYAxis(axisRight)
customizeYAxis(axisLeft)
val formatter = object : ValueFormatter() {
override fun getAxisLabel(value: Float, axis: AxisBase?): String {
val index = value.toInt() % Months.values().size
return Months.values()[index].name
}
}
xAxis.apply {
setDrawGridLines(false)
setDrawAxisLine(false)
position = XAxis.XAxisPosition.BOTTOM
axisMinimum = 0f - 0.25f
granularity = 1f
valueFormatter = formatter
axisMaximum = combinedData.xMax + 0.25f
}
}
}

private fun customizeYAxis(axis: YAxis) {
axis.apply {
setDrawGridLines(false)
setDrawAxisLine(false)
setDrawLabels(false)
axisMinimum = 0f
}
}

private fun generateLineData(dataList: MutableList<Entry>): LineData {
val lineData = LineData()
val lineDataSet = LineDataSet(dataList, "Line DataSet")
lineDataSet.apply {
color = resources.getColor(R.color.purple_200, resources.newTheme())
valueTextSize = 12f
valueTextColor = Color.BLACK
setDrawValues(true)
mode = LineDataSet.Mode.CUBIC_BEZIER
lineWidth = 2f
axisDependency = YAxis.AxisDependency.LEFT
}
lineData.addDataSet(lineDataSet)
return lineData
}

private fun generateBarData(dataList: MutableList<BarEntry>): BarData {
val dataSet = BarDataSet(dataList, "Bar")
dataSet.apply {
color = ResourcesCompat.getColor(resources, R.color.light_green, resources.newTheme())
valueTextColor = Color.TRANSPARENT
dataSet.valueTextSize = 10f
axisDependency = YAxis.AxisDependency.LEFT
}
val barWidth = 0.45f
val barData = BarData(dataSet)
barData.barWidth = barWidth
return barData
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
package com.example.cyclofit.ui.fragment

import android.content.Context
import android.graphics.Color
import android.graphics.Typeface
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.example.cyclofit.databinding.FragmentDistancePieChartBinding
import com.example.cyclofit.model.Shared
import com.example.cyclofit.ui.utils.ChartUtils
import com.example.cyclofit.ui.utils.Constants
import com.github.mikephil.charting.animation.Easing
import com.github.mikephil.charting.components.Legend
import com.github.mikephil.charting.data.Entry
import com.github.mikephil.charting.data.PieData
import com.github.mikephil.charting.data.PieDataSet
import com.github.mikephil.charting.data.PieEntry
import com.github.mikephil.charting.formatter.ValueFormatter
import com.github.mikephil.charting.highlight.Highlight
import com.github.mikephil.charting.listener.OnChartValueSelectedListener
import com.github.mikephil.charting.utils.MPPointF
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.lang.reflect.Type

class DistancePieChartFragment : Fragment(), OnChartValueSelectedListener {
lateinit var binding: FragmentDistancePieChartBinding
var sp = ArrayList<Shared>()

override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
binding = FragmentDistancePieChartBinding.inflate(inflater, container, false)
val sharedPreferences1 = requireContext().getSharedPreferences(
Constants.CYCLOFIT_PREFERENCES,
Context.MODE_PRIVATE
)
val gson = Gson()
val json = sharedPreferences1.getString("timer", null)
val type: Type = object : TypeToken<ArrayList<Shared?>?>() {}.type

if (json != null) {
sp = gson.fromJson(json, type)
}

val distanceArrayList = ArrayList<String>()
for (i in 0 until sp.size) {
distanceArrayList.add("0")
}
val pieDataList = mutableListOf<PieEntry>()
for ((x, i) in distanceArrayList.withIndex()) {
pieDataList.add(PieEntry(i.toFloat(), "${i.toFloat()} km"))
}
setPieChart(pieDataList)
return binding.root
}

private fun setPieChart(dataList: MutableList<PieEntry>) {
val dataSet = PieDataSet(dataList, "My Graph")
dataSet.apply {
valueTextSize = 16f
valueTextColor = Color.BLACK
setDrawIcons(false)
sliceSpace = 3f
iconsOffset = MPPointF(0f, 40f)
selectionShift = 5f
colors = ChartUtils.colors
}

binding.pieChart.apply {
isDrawHoleEnabled = false
description.isEnabled = false
setUsePercentValues(true)

// entry label text styling
setEntryLabelColor(Color.TRANSPARENT) // hide label text in Pie chart
setEntryLabelTypeface(Typeface.DEFAULT)
setEntryLabelTextSize(16f)

dragDecelerationFrictionCoef = 0.95f
rotationAngle = 0f
// enable rotation of the chart by touch
isRotationEnabled = true
isHighlightPerTapEnabled = true

animateY(1400, Easing.EaseInOutQuad)

legend.apply {
verticalAlignment = Legend.LegendVerticalAlignment.TOP
horizontalAlignment = Legend.LegendHorizontalAlignment.RIGHT
orientation = Legend.LegendOrientation.VERTICAL
setDrawInside(false)
xEntrySpace = 7f
yEntrySpace = 0f
yOffset = 0f
}
val pieData = PieData(dataSet).apply {
setValueFormatter(object : ValueFormatter() {
override fun getPieLabel(value: Float, pieEntry: PieEntry?): String {
return "${String.format("%.1f", value)} %"
}
})
}
data = PieData(dataSet)
highlightValues(null) // undo all highlights
invalidate()
}
// add a selection listener
binding.pieChart.setOnChartValueSelectedListener(this)
}

override fun onValueSelected(e: Entry?, h: Highlight?) {
if (e == null)
return
}

override fun onNothingSelected() {
return
}
}
Loading

0 comments on commit 2ad6b2b

Please sign in to comment.