mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-04-22 10:52:13 +00:00
feat: Change inner work to allow more depictions of datas
This commit is contained in:
parent
be055fd1be
commit
e3313a65a8
@ -29,7 +29,7 @@ Permissions requests are for specifics usage and are only requests the first tim
|
|||||||
| Permission | Why is it requested |
|
| Permission | Why is it requested |
|
||||||
|:--------------------:|:-----------------------------------------------------------|
|
|:--------------------:|:-----------------------------------------------------------|
|
||||||
| ACTIVITY_RECOGNITION | Device Steps Usage |
|
| ACTIVITY_RECOGNITION | Device Steps Usage |
|
||||||
| INTERNET | Food fetching from OpenFoodFact |
|
| INTERNET | Food fetching from OpenFoodFact |
|
||||||
| POST_NOTIFICATIONS | send notifications for water intake and device steps usage |
|
| POST_NOTIFICATIONS | send notifications for water intake and device steps usage |
|
||||||
|
|
||||||
No other permissions are used.
|
No other permissions are used.
|
||||||
|
94
app/src/debug/res/layout/fragment_list_weight.xml
Normal file
94
app/src/debug/res/layout/fragment_list_weight.xml
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<com.google.android.material.card.MaterialCardView
|
||||||
|
style="?attr/materialCardViewFilledStyle"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="16dp"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:layout_marginEnd="16dp">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
|
||||||
|
android:paddingHorizontal="12dp"
|
||||||
|
android:paddingVertical="16dp">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:layout_width="40dp"
|
||||||
|
android:layout_height="40dp"
|
||||||
|
android:paddingVertical="8dp"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:src="@drawable/ic_outline_monitor_weight_24"
|
||||||
|
android:background="@drawable/shape_circle"
|
||||||
|
app:tint="?colorOnPrimary" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="4dp"
|
||||||
|
android:text="History" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<com.dzeio.charts.ChartView
|
||||||
|
android:id="@+id/chart"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="200dp"
|
||||||
|
android:minHeight="200dp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/goal_button"
|
||||||
|
android:text="@string/add_goal"
|
||||||
|
style="?materialButtonOutlinedStyle"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="end"
|
||||||
|
android:layout_marginEnd="16dp"
|
||||||
|
android:layout_marginVertical="16dp"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</com.google.android.material.card.MaterialCardView>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/debug_random_values"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="Generate random values" />
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:id="@+id/list"
|
||||||
|
android:padding="16dp"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
|
||||||
|
tools:listitem="@layout/layout_item_list"
|
||||||
|
tools:context=".ui.weight.ListWeightFragment" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -88,9 +88,7 @@ class StepsHomeFragment :
|
|||||||
}
|
}
|
||||||
|
|
||||||
xAxis.apply {
|
xAxis.apply {
|
||||||
increment = 86400000.0
|
dataWidth = 604800000.0
|
||||||
// displayCount = 168
|
|
||||||
// displayCount = 10
|
|
||||||
textPaint.color = MaterialColors.getColor(
|
textPaint.color = MaterialColors.getColor(
|
||||||
requireView(),
|
requireView(),
|
||||||
com.google.android.material.R.attr.colorOnPrimaryContainer
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
|
@ -10,15 +10,18 @@ import android.view.ViewGroup
|
|||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import com.dzeio.charts.Entry
|
||||||
|
import com.dzeio.charts.series.LineSerie
|
||||||
import com.dzeio.openhealth.R
|
import com.dzeio.openhealth.R
|
||||||
import com.dzeio.openhealth.adapters.WeightAdapter
|
import com.dzeio.openhealth.adapters.WeightAdapter
|
||||||
import com.dzeio.openhealth.core.BaseFragment
|
import com.dzeio.openhealth.core.BaseFragment
|
||||||
import com.dzeio.openhealth.data.weight.Weight
|
import com.dzeio.openhealth.data.weight.Weight
|
||||||
import com.dzeio.openhealth.databinding.FragmentListWeightBinding
|
import com.dzeio.openhealth.databinding.FragmentListWeightBinding
|
||||||
import com.dzeio.openhealth.graphs.WeightChart
|
|
||||||
import com.dzeio.openhealth.utils.GraphUtils
|
|
||||||
import com.google.android.material.color.MaterialColors
|
import com.google.android.material.color.MaterialColors
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
import java.text.DateFormat
|
||||||
|
import java.util.Date
|
||||||
|
import java.util.Locale
|
||||||
|
|
||||||
@AndroidEntryPoint
|
@AndroidEntryPoint
|
||||||
class ListWeightFragment :
|
class ListWeightFragment :
|
||||||
@ -57,7 +60,7 @@ class ListWeightFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val recycler = binding.list.apply {
|
binding.list.apply {
|
||||||
val manager = LinearLayoutManager(requireContext())
|
val manager = LinearLayoutManager(requireContext())
|
||||||
layoutManager = manager
|
layoutManager = manager
|
||||||
this.adapter = adapter
|
this.adapter = adapter
|
||||||
@ -65,7 +68,6 @@ class ListWeightFragment :
|
|||||||
|
|
||||||
viewModel.massUnit.observe(viewLifecycleOwner) {
|
viewModel.massUnit.observe(viewLifecycleOwner) {
|
||||||
adapter.unit = it
|
adapter.unit = it
|
||||||
// adapter.notifyDataSetChanged()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
viewModel.weights.observe(viewLifecycleOwner) {
|
viewModel.weights.observe(viewLifecycleOwner) {
|
||||||
@ -77,28 +79,88 @@ class ListWeightFragment :
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GraphUtils.lineChartSetup(
|
val chart = binding.chart
|
||||||
binding.chart,
|
|
||||||
MaterialColors.getColor(
|
val serie = LineSerie(chart).apply {
|
||||||
|
linePaint.color = MaterialColors.getColor(
|
||||||
requireView(),
|
requireView(),
|
||||||
com.google.android.material.R.attr.colorPrimary
|
com.google.android.material.R.attr.colorPrimary
|
||||||
),
|
|
||||||
MaterialColors.getColor(
|
|
||||||
requireView(),
|
|
||||||
com.google.android.material.R.attr.colorOnBackground
|
|
||||||
)
|
)
|
||||||
)
|
textPaint.color = MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorOnPrimary
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.apply {
|
||||||
|
series = arrayListOf(serie)
|
||||||
|
|
||||||
|
yAxis.apply {
|
||||||
|
textLabel.color = MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
|
)
|
||||||
|
linePaint.color = MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
|
)
|
||||||
|
|
||||||
|
onValueFormat = { value -> "${value.toInt()}" }
|
||||||
|
}
|
||||||
|
|
||||||
|
xAxis.apply {
|
||||||
|
// 7 day history
|
||||||
|
// increment = (7 * 24 * 60 * 60 * 1000).toDouble()
|
||||||
|
textPaint.color = MaterialColors.getColor(
|
||||||
|
requireView(),
|
||||||
|
com.google.android.material.R.attr.colorOnPrimaryContainer
|
||||||
|
)
|
||||||
|
textPaint.textSize = 32f
|
||||||
|
onValueFormat = onValueFormat@{
|
||||||
|
val formatter = DateFormat.getDateTimeInstance(
|
||||||
|
DateFormat.SHORT,
|
||||||
|
DateFormat.SHORT,
|
||||||
|
Locale.getDefault()
|
||||||
|
)
|
||||||
|
return@onValueFormat formatter.format(Date(it.toLong()))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug button
|
||||||
|
if (binding.debugRandomValues != null) {
|
||||||
|
binding.debugRandomValues.setOnClickListener {
|
||||||
|
viewModel.generateRandomValues()
|
||||||
|
}
|
||||||
|
binding.debugRandomValues.setOnLongClickListener {
|
||||||
|
viewModel.delete(viewModel.weights.value!!)
|
||||||
|
return@setOnLongClickListener true
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateGraph(list: List<Weight>) {
|
private fun updateGraph(list: List<Weight>) {
|
||||||
WeightChart.setup(
|
val chart = binding.chart
|
||||||
binding.chart,
|
val serie = chart.series[0] as LineSerie
|
||||||
requireView(),
|
|
||||||
list,
|
val entries: ArrayList<Entry> = arrayListOf()
|
||||||
viewModel.massUnit.value!!,
|
|
||||||
viewModel.goalWeight.value,
|
list.forEach {
|
||||||
false
|
entries.add(Entry(
|
||||||
)
|
it.timestamp.toDouble(),
|
||||||
|
it.weight
|
||||||
|
))
|
||||||
|
}
|
||||||
|
serie.entries = entries
|
||||||
|
|
||||||
|
if (list.isEmpty()) {
|
||||||
|
chart.xAxis.x = 0.0
|
||||||
|
} else {
|
||||||
|
chart.xAxis.x = list[0].timestamp.toDouble()
|
||||||
|
}
|
||||||
|
|
||||||
|
chart.refresh()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Deprecated("Deprecated in Java")
|
@Deprecated("Deprecated in Java")
|
||||||
|
@ -13,6 +13,7 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||||||
import kotlinx.coroutines.flow.collectLatest
|
import kotlinx.coroutines.flow.collectLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class ListWeightViewModel @Inject internal constructor(
|
class ListWeightViewModel @Inject internal constructor(
|
||||||
@ -48,4 +49,20 @@ class ListWeightViewModel @Inject internal constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun generateRandomValues(): Unit {
|
||||||
|
viewModelScope.launch {
|
||||||
|
weightRepository.addWeight(
|
||||||
|
Weight(
|
||||||
|
weight = Random.nextInt(0, 100).toFloat()
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun delete(list: List<Weight>) {
|
||||||
|
viewModelScope.launch {
|
||||||
|
for (item in list) weightRepository.deleteWeight(item)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,7 @@ object Units {
|
|||||||
);
|
);
|
||||||
|
|
||||||
fun formatToString(value: Int): String {
|
fun formatToString(value: Int): String {
|
||||||
return String.format("%d", value * modifier)
|
return String.format("%.0f", (value * modifier))
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<array name="health_permissions">
|
|
||||||
<item>androidx.health.permission.HeartRate.READ</item>
|
|
||||||
<item>androidx.health.permission.HeartRate.WRITE</item>
|
|
||||||
<item>androidx.health.permission.Steps.READ</item>
|
|
||||||
<item>androidx.health.permission.Steps.WRITE</item>
|
|
||||||
</array>
|
|
||||||
</resources>
|
|
@ -34,9 +34,7 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
|||||||
private val scroller = ChartScroll(this).apply {
|
private val scroller = ChartScroll(this).apply {
|
||||||
var lastMovement = 0.0
|
var lastMovement = 0.0
|
||||||
setOnChartMoved { movementX, _ ->
|
setOnChartMoved { movementX, _ ->
|
||||||
|
xAxis.x += (movementX - lastMovement) * xAxis.getDataWidth() / width
|
||||||
// Log.d(TAG, "scrolled: ${(movementX - lastMovement) * (xAxis.increment / 10)}")
|
|
||||||
xAxis.x = xAxis.x + (movementX - lastMovement) * (xAxis.increment * xAxis.displayCount / width)
|
|
||||||
lastMovement = movementX.toDouble()
|
lastMovement = movementX.toDouble()
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
@ -140,4 +138,13 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
|||||||
performClick()
|
performClick()
|
||||||
return scroller.onTouchEvent(event)
|
return scroller.onTouchEvent(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getDataset(): ArrayList<Entry> {
|
||||||
|
val data: ArrayList<Entry> = arrayListOf()
|
||||||
|
for (serie in series) {
|
||||||
|
data.addAll(serie.entries)
|
||||||
|
}
|
||||||
|
data.sortBy { it.x }
|
||||||
|
return data.filterIndexed { index, entry -> data.indexOf(entry) == index } as ArrayList<Entry>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -39,4 +39,9 @@ interface ChartViewInterface {
|
|||||||
* this function should be run if you change parameters in the view
|
* this function should be run if you change parameters in the view
|
||||||
*/
|
*/
|
||||||
fun refresh()
|
fun refresh()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the whole dataset (sorted and cleaned up of dupps)
|
||||||
|
*/
|
||||||
|
fun getDataset(): ArrayList<Entry>
|
||||||
}
|
}
|
@ -13,7 +13,7 @@ class XAxis(
|
|||||||
) : XAxisInterface {
|
) : XAxisInterface {
|
||||||
override var x: Double = 0.0
|
override var x: Double = 0.0
|
||||||
set(value) {
|
set(value) {
|
||||||
val max = getXMax() - increment * displayCount
|
val max = getXMax() - getDataWidth()
|
||||||
val min = getXMin()
|
val min = getXMin()
|
||||||
if (value > max && min <= max) {
|
if (value > max && min <= max) {
|
||||||
field = max
|
field = max
|
||||||
@ -29,10 +29,13 @@ class XAxis(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override var enabled = true
|
override var enabled = true
|
||||||
override var increment: Double = 1.0
|
|
||||||
override var displayCount: Int = 10
|
override var dataWidth: Double? = null
|
||||||
|
|
||||||
override var labelCount: Int = 2
|
override var labelCount: Int = 2
|
||||||
|
|
||||||
|
var spacing = 16.0
|
||||||
|
|
||||||
override val textPaint = Paint().apply {
|
override val textPaint = Paint().apply {
|
||||||
isAntiAlias = true
|
isAntiAlias = true
|
||||||
color = Color.parseColor("#FC496D")
|
color = Color.parseColor("#FC496D")
|
||||||
@ -42,27 +45,28 @@ class XAxis(
|
|||||||
|
|
||||||
private val rect = Rect()
|
private val rect = Rect()
|
||||||
|
|
||||||
override fun getPositionOnRect(entry: Entry, rect: RectF): Double {
|
override fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Double {
|
||||||
return translatePositionToRect(entry.x, rect)
|
return translatePositionToRect(entry.x, drawableSpace)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translatePositionToRect(value: Double, rect: RectF): Double {
|
fun translatePositionToRect(value: Double, drawableSpace: RectF): Double {
|
||||||
val rectItem = rect.width() / displayCount // item size in graph
|
return drawableSpace.width() * (value - x) / getDataWidth()
|
||||||
return rectItem * value / increment
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getXOffset(rect: RectF): Double {
|
|
||||||
return translatePositionToRect(x, rect)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getXMax(): Double {
|
override fun getXMax(): Double {
|
||||||
return view.series.maxOf { serie ->
|
return view.series.maxOf { serie ->
|
||||||
|
if (serie.entries.isEmpty()) {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
serie.entries.maxOf { entry -> entry.x }
|
serie.entries.maxOf { entry -> entry.x }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun getXMin(): Double {
|
override fun getXMin(): Double {
|
||||||
return view.series.minOf { serie ->
|
return view.series.minOf { serie ->
|
||||||
|
if (serie.entries.isEmpty()) {
|
||||||
|
return 0.0
|
||||||
|
}
|
||||||
serie.entries.minOf { entry -> entry.x }
|
serie.entries.minOf { entry -> entry.x }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,7 +81,7 @@ class XAxis(
|
|||||||
var maxHeight = 0f
|
var maxHeight = 0f
|
||||||
|
|
||||||
val graphIncrement = space.width() / (labelCount - 1)
|
val graphIncrement = space.width() / (labelCount - 1)
|
||||||
val valueIncrement = (displayCount * increment / (labelCount - 1)).toDouble()
|
val valueIncrement = (getDataWidth() / (labelCount - 1)).toDouble()
|
||||||
for (index in 0 until labelCount) {
|
for (index in 0 until labelCount) {
|
||||||
val text = onValueFormat(x + valueIncrement * index)
|
val text = onValueFormat(x + valueIncrement * index)
|
||||||
textPaint.getTextBounds(text, 0, text.length, rect)
|
textPaint.getTextBounds(text, 0, text.length, rect)
|
||||||
@ -89,7 +93,6 @@ class XAxis(
|
|||||||
xPos = space.right - rect.width()
|
xPos = space.right - rect.width()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
canvas.drawText(
|
canvas.drawText(
|
||||||
text,
|
text,
|
||||||
xPos,
|
xPos,
|
||||||
@ -103,4 +106,20 @@ class XAxis(
|
|||||||
override fun refresh() {
|
override fun refresh() {
|
||||||
// TODO("Not yet implemented")
|
// TODO("Not yet implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun getEntryWidth(drawableSpace: RectF): Double {
|
||||||
|
var smallest = -1.0
|
||||||
|
val dataset = view.getDataset()
|
||||||
|
for (idx in 0 until dataset.size - 1) {
|
||||||
|
val distance = dataset[idx + 1].x - dataset[idx].x
|
||||||
|
if (smallest == -1.0 || smallest > distance) {
|
||||||
|
smallest = distance
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return drawableSpace.width() * smallest / getDataWidth() - spacing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getDataWidth(): Double {
|
||||||
|
return dataWidth ?: (getXMax() - getXMin())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,20 +18,19 @@ sealed interface XAxisInterface {
|
|||||||
var x: Double
|
var x: Double
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* X increment
|
* the "width" of the graph
|
||||||
|
*
|
||||||
|
* if not set it will be `XMax - XMin`
|
||||||
|
*
|
||||||
|
* ex: to display a 7 days graph history with x values being timestamp in secs, use 7*24*60*60
|
||||||
*/
|
*/
|
||||||
var increment: Double
|
var dataWidth: Double?
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* text Paint
|
* text Paint
|
||||||
*/
|
*/
|
||||||
val textPaint: Paint
|
val textPaint: Paint
|
||||||
|
|
||||||
/**
|
|
||||||
* indicate the max number of entries are displayed
|
|
||||||
*/
|
|
||||||
var displayCount: Int
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* indicate the number of labels displayed
|
* indicate the number of labels displayed
|
||||||
*/
|
*/
|
||||||
@ -49,12 +48,7 @@ sealed interface XAxisInterface {
|
|||||||
*
|
*
|
||||||
* @return the left side of the position of the entry
|
* @return the left side of the position of the entry
|
||||||
*/
|
*/
|
||||||
fun getPositionOnRect(entry: Entry, rect: RectF): Double
|
fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Double
|
||||||
|
|
||||||
/**
|
|
||||||
* get the graph offset for X (kinda like [getPositionOnRect])
|
|
||||||
*/
|
|
||||||
fun getXOffset(rect: RectF): Double
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get the maximum the X can get to
|
* get the maximum the X can get to
|
||||||
@ -66,6 +60,18 @@ sealed interface XAxisInterface {
|
|||||||
*/
|
*/
|
||||||
fun getXMin(): Double
|
fun getXMin(): Double
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the size of an entry in the graph
|
||||||
|
*
|
||||||
|
* @return the size in [drawableSpace] px
|
||||||
|
*/
|
||||||
|
fun getEntryWidth(drawableSpace: RectF): Double
|
||||||
|
|
||||||
|
/**
|
||||||
|
* return the currently used dataWidth
|
||||||
|
*/
|
||||||
|
fun getDataWidth(): Double
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* onDraw event that will draw the XAxis
|
* onDraw event that will draw the XAxis
|
||||||
*
|
*
|
||||||
|
@ -5,6 +5,7 @@ import android.graphics.Color
|
|||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
|
import android.util.Log
|
||||||
import com.dzeio.charts.ChartView
|
import com.dzeio.charts.ChartView
|
||||||
import com.dzeio.charts.utils.drawRoundRect
|
import com.dzeio.charts.utils.drawRoundRect
|
||||||
|
|
||||||
@ -35,9 +36,8 @@ class BarSerie(
|
|||||||
private val rect = Rect()
|
private val rect = Rect()
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas, drawableSpace: RectF) {
|
override fun onDraw(canvas: Canvas, drawableSpace: RectF) {
|
||||||
val spacing = drawableSpace.width() / view.xAxis.displayCount / 10
|
|
||||||
val barWidth = drawableSpace.width() / view.xAxis.displayCount - spacing
|
|
||||||
val displayedEntries = getDisplayedEntries()
|
val displayedEntries = getDisplayedEntries()
|
||||||
|
val barWidth = view.xAxis.getEntryWidth(drawableSpace).toFloat()
|
||||||
val max = view.yAxis.getYMax()
|
val max = view.yAxis.getYMax()
|
||||||
val min = view.yAxis.getYMin()
|
val min = view.yAxis.getYMin()
|
||||||
|
|
||||||
@ -46,21 +46,10 @@ class BarSerie(
|
|||||||
for (entry in displayedEntries) {
|
for (entry in displayedEntries) {
|
||||||
// calculated height in percent from 0 to 100
|
// calculated height in percent from 0 to 100
|
||||||
val top = (1 - entry.y / max) * drawableSpace.height() + drawableSpace.top
|
val top = (1 - entry.y / max) * drawableSpace.height() + drawableSpace.top
|
||||||
var posX = drawableSpace.left + (view.xAxis.getPositionOnRect(
|
var posX = drawableSpace.left + view.xAxis.getPositionOnRect(
|
||||||
entry,
|
entry,
|
||||||
drawableSpace
|
drawableSpace
|
||||||
) - view.xAxis.getXOffset(drawableSpace)).toFloat()
|
).toFloat()
|
||||||
// Log.d(TAG, "gpor = ${view.xAxis.getPositionOnRect(entry, space)}, gxo = ${view.xAxis.getXOffset(space)}")
|
|
||||||
// Log.d(TAG, "max = $max, y = ${entry.y}, height = $height")
|
|
||||||
// Log.d(TAG, "posX: ${posX / 60 / 60 / 1000}, offsetX = ${view.xAxis.x / (1000 * 60 * 60)}, x = ${entry.x / (1000 * 60 * 60)}, pouet: ${(view.xAxis.x + view.xAxis.displayCount * view.xAxis.increment) / (1000 * 60 * 60)}")
|
|
||||||
|
|
||||||
// Log.d(
|
|
||||||
// TAG, """
|
|
||||||
// ${posX},
|
|
||||||
// $top,
|
|
||||||
// ${(posX + barWidth)},
|
|
||||||
// ${drawableSpace.bottom}""".trimIndent()
|
|
||||||
// )
|
|
||||||
|
|
||||||
val right = (posX + barWidth).coerceAtMost(drawableSpace.right)
|
val right = (posX + barWidth).coerceAtMost(drawableSpace.right)
|
||||||
|
|
||||||
|
@ -19,14 +19,28 @@ sealed class BaseSerie(
|
|||||||
override var entries: ArrayList<Entry> = arrayListOf()
|
override var entries: ArrayList<Entry> = arrayListOf()
|
||||||
|
|
||||||
override fun getDisplayedEntries(): ArrayList<Entry> {
|
override fun getDisplayedEntries(): ArrayList<Entry> {
|
||||||
// -+ view.xAxis.increment = one out of display
|
val minX = view.xAxis.x
|
||||||
val minX = view.xAxis.x - view.xAxis.increment
|
val maxX = minX + view.xAxis.getDataWidth()
|
||||||
val maxX =
|
|
||||||
view.xAxis.x + view.xAxis.displayCount * view.xAxis.increment + view.xAxis.increment
|
|
||||||
|
|
||||||
return entries.filter {
|
val result: ArrayList<Entry> = arrayListOf()
|
||||||
return@filter it.x in minX..maxX
|
|
||||||
} as ArrayList<Entry>
|
var lastIndex = -1
|
||||||
|
for (i in 0 until entries.size) {
|
||||||
|
val it = entries[i]
|
||||||
|
if (it.x in minX..maxX) {
|
||||||
|
if (result.size === 0 && i > 0) {
|
||||||
|
result.add((entries[i - 1]))
|
||||||
|
}
|
||||||
|
lastIndex = i
|
||||||
|
result.add(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastIndex < entries.size - 1) {
|
||||||
|
result.add(entries [lastIndex + 1])
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract override fun onDraw(canvas: Canvas, drawableSpace: RectF)
|
abstract override fun onDraw(canvas: Canvas, drawableSpace: RectF)
|
||||||
|
74
charts/src/main/java/com/dzeio/charts/series/LineSerie.kt
Normal file
74
charts/src/main/java/com/dzeio/charts/series/LineSerie.kt
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
package com.dzeio.charts.series
|
||||||
|
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.RectF
|
||||||
|
import com.dzeio.charts.ChartView
|
||||||
|
|
||||||
|
class LineSerie(
|
||||||
|
private val view: ChartView
|
||||||
|
) : BaseSerie(view) {
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TAG = "Charts/LineSerie"
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
view.series.add(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
val linePaint = Paint().apply {
|
||||||
|
isAntiAlias = true
|
||||||
|
color = Color.parseColor("#123456")
|
||||||
|
strokeWidth = 5f
|
||||||
|
}
|
||||||
|
|
||||||
|
val textPaint = Paint().apply {
|
||||||
|
isAntiAlias = true
|
||||||
|
color = Color.parseColor("#FC496D")
|
||||||
|
textSize = 30f
|
||||||
|
textAlign = Paint.Align.CENTER
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas, drawableSpace: RectF) {
|
||||||
|
val displayedEntries = getDisplayedEntries()
|
||||||
|
displayedEntries.sortBy { it.x }
|
||||||
|
val max = view.yAxis.getYMax()
|
||||||
|
|
||||||
|
var previousPosX: Float? = null
|
||||||
|
var previousPosY: Float? = null
|
||||||
|
|
||||||
|
for (entry in displayedEntries) {
|
||||||
|
// calculated height in percent from 0 to 100
|
||||||
|
val top = (1 - entry.y / max) * drawableSpace.height() + drawableSpace.top
|
||||||
|
val posX = (drawableSpace.left +
|
||||||
|
view.xAxis.getPositionOnRect(entry, drawableSpace) +
|
||||||
|
view.xAxis.getEntryWidth(drawableSpace) / 2f).toFloat()
|
||||||
|
|
||||||
|
// handle color recoloration
|
||||||
|
val paint = Paint(linePaint)
|
||||||
|
|
||||||
|
if (entry.color != null) {
|
||||||
|
paint.color = entry.color!!
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw smol point
|
||||||
|
if (posX < drawableSpace.right) {
|
||||||
|
canvas.drawCircle(posX, top, paint.strokeWidth, paint)
|
||||||
|
}
|
||||||
|
|
||||||
|
// draw line
|
||||||
|
if (previousPosX != null && previousPosY != null) {
|
||||||
|
canvas.drawLine(previousPosX, previousPosY, posX, top, paint)
|
||||||
|
|
||||||
|
}
|
||||||
|
previousPosX = posX
|
||||||
|
previousPosY = top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun refresh() {
|
||||||
|
// TODO("Not yet implemented")
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user