-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add Pie and Combined(bar + line) chart in HealthFragment
- 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
1 parent
3bf7051
commit 9ebb85d
Showing
20 changed files
with
1,246 additions
and
156 deletions.
There are no files selected for viewing
24 changes: 24 additions & 0 deletions
24
app/src/main/java/com/example/cyclofit/ui/adapter/CombinedChartAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.example.cyclofit.ui.adapter | ||
|
||
import androidx.fragment.app.Fragment | ||
import androidx.fragment.app.FragmentManager | ||
import androidx.lifecycle.Lifecycle | ||
import androidx.viewpager2.adapter.FragmentStateAdapter | ||
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) : | ||
FragmentStateAdapter(fragmentManager, lifecycle) { | ||
override fun getItemCount(): Int { | ||
return 3 | ||
} | ||
|
||
override fun createFragment(position: Int): Fragment { | ||
return when (position) { | ||
0 -> KcalCombinedChartFragment() | ||
1 -> TimeCombinedChartFragment() | ||
else -> DistanceCombineChartFragment() | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
app/src/main/java/com/example/cyclofit/ui/adapter/PieChartAdapter.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.example.cyclofit.ui.adapter | ||
|
||
import androidx.fragment.app.Fragment | ||
import androidx.fragment.app.FragmentManager | ||
import androidx.lifecycle.Lifecycle | ||
import androidx.viewpager2.adapter.FragmentStateAdapter | ||
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) : | ||
FragmentStateAdapter(fragmentManager, lifecycle) { | ||
override fun getItemCount(): Int { | ||
return 3 | ||
} | ||
|
||
override fun createFragment(position: Int): Fragment { | ||
return when (position) { | ||
0 -> KcalPeiChartFragment() | ||
1 -> TimePieCharFragment() | ||
else -> DistancePieChartFragment() | ||
} | ||
} | ||
} |
158 changes: 158 additions & 0 deletions
158
app/src/main/java/com/example/cyclofit/ui/fragment/DistanceCombineChartFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,158 @@ | ||
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.ChartUtils.months | ||
import com.example.cyclofit.ui.utils.Constants | ||
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) | ||
} | ||
axisRight.apply { | ||
setDrawGridLines(false) | ||
setDrawAxisLine(false) | ||
setDrawLabels(false) | ||
axisMinimum = 0f | ||
} | ||
axisLeft.apply { | ||
setDrawGridLines(false) | ||
setDrawAxisLine(false) | ||
setDrawLabels(false) | ||
axisMinimum = 0f | ||
} | ||
val formatter = object : ValueFormatter() { | ||
override fun getAxisLabel(value: Float, axis: AxisBase?): String { | ||
val index = value.toInt() % months.size | ||
return months[index] | ||
} | ||
} | ||
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 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 // x2 dataset | ||
// (0.45 + 0.02) * 2 + 0.06 = 1.00 -> interval per "group" | ||
val barData = BarData(dataSet) | ||
barData.barWidth = barWidth | ||
return barData | ||
} | ||
} |
124 changes: 124 additions & 0 deletions
124
app/src/main/java/com/example/cyclofit/ui/fragment/DistancePieChartFragment.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} | ||
} |
Oops, something went wrong.