1
0
mirror of https://github.com/dzeiocom/OpenHealth.git synced 2025-04-22 19:02:16 +00:00

feat(charts): Add back the XAxis/YAxis/Scrolling

This commit is contained in:
Florian Bouillon 2022-08-23 00:59:10 +02:00
parent 07d150968e
commit 39f6e7cbef
17 changed files with 331 additions and 447 deletions

View File

@ -13,6 +13,9 @@ import com.dzeio.openhealth.core.BaseFragment
import com.dzeio.openhealth.databinding.FragmentStepsHomeBinding
import com.google.android.material.color.MaterialColors
import dagger.hilt.android.AndroidEntryPoint
import java.text.DateFormat
import java.util.Date
import java.util.Locale
@AndroidEntryPoint
class StepsHomeFragment :
@ -61,10 +64,10 @@ class StepsHomeFragment :
chart.apply {
series = arrayListOf(serie)
debug = true
// debug = true
yAxis.apply {
setYMin(0f)
setYMax(500f)
textLabel.color = MaterialColors.getColor(
requireView(),
com.google.android.material.R.attr.colorOnPrimaryContainer
@ -79,27 +82,34 @@ class StepsHomeFragment :
xAxis.apply {
increment = 3600000.0
displayCount = 168
// displayCount = 24
// displayCount = 168
displayCount = 10
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()))
}
}
}
viewModel.items.observe(viewLifecycleOwner) { list ->
adapter.set(list)
chart.debug = true
// chart.animation.enabled = false
// chart.animation.refreshRate = 60
// chart.animation.duration = 300
// chart.scroller.zoomEnabled = false
// chart.xAxis.labels.color = MaterialColors.getColor(
// requireView(),
// com.google.android.material.R.attr.colorOnBackground
// )
// chart.xAxis.labels.size = 32f
serie.entries = list.reversed().map {
@ -108,32 +118,8 @@ class StepsHomeFragment :
chart.xAxis.x = serie.entries.first().x
// chart.xAxis.onValueFormat = onValueFormat@{
// val formatter = DateFormat.getDateTimeInstance(
// DateFormat.SHORT,
// DateFormat.SHORT,
// Locale.getDefault()
// )
// return@onValueFormat formatter.format(Date(it.toLong()))
// }
chart.refresh()
}
// val scrollView = requireActivity().findViewById<NestedScrollView>(R.id.scrollView)
// var scrollEnabled = false
// scrollView.setOnTouchListener { view, _ ->
// view.performClick()
// if (scrollEnabled) {
// } else {
// return@setOnTouchListener !scrollEnabled
// }
// return@setOnTouchListener true
// }
// binding.chart.setOnToggleScroll {
// Log.d(TAG, it.toString())
// scrollEnabled = it
// }
}
}

View File

@ -1,12 +1,12 @@
package com.dzeio.charts
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import com.dzeio.charts.axis.XAxis
@ -21,14 +21,6 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
const val TAG = "Charts/ChartView"
}
private val rect = RectF()
private val debugStrokePaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 8f
color = Color.parseColor("#654321")
}
override var debug: Boolean = false
override val xAxis = XAxis(this)
@ -37,43 +29,13 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
override var series: ArrayList<SerieInterface> = arrayListOf()
override fun refresh() {
invalidate()
}
override var padding: Float = 8f
override fun onDraw(canvas: Canvas) {
if (debug) {
// draw corners
canvas.drawRect(rect.apply {
set(8f, 8f, width - 8f, height - 8f)
}, debugStrokePaint)
}
// right distance from the yAxis
val rightDistance = yAxis.onDraw(canvas)
// chart draw rectangle
rect.apply {
set(0f, 8f, width.toFloat() - 16f - rightDistance, height.toFloat() - 16f)
}
for (serie in series) {
serie.onDraw(canvas, rect)
}
super.onDraw(canvas)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
performClick()
return scroller.onTouchEvent(event)
}
val scroller = ChartScroll(this).apply {
private val scroller = ChartScroll(this).apply {
var lastMovement = 0.0
setOnChartMoved { movementX, _ ->
Log.d(TAG, "scrolled: ${(movementX - lastMovement) * (xAxis.increment / 10)}")
// Log.d(TAG, "scrolled: ${(movementX - lastMovement) * (xAxis.increment / 10)}")
xAxis.x = xAxis.x + (movementX - lastMovement) * (xAxis.increment / 10)
lastMovement = movementX.toDouble()
refresh()
@ -84,4 +46,98 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
// refresh()
// }
}
}
// val animator: Runnable = object : Runnable {
// override fun run() {
// var needNewFrame = false
// for (serie in series) {
// val result = serie.onUpdate()
// if (result) {
// needNewFrame = true
// }
// }
// if (needNewFrame) {
// postDelayed(this, animation.getDelay().toLong())
// }
// invalidate()
// }
// }
// rect used for calculations
private val rect = RectF()
// stroke used while in debug
private val debugStrokePaint = Paint().apply {
style = Paint.Style.STROKE
strokeWidth = 8f
color = Color.parseColor("#654321")
}
override fun refresh() {
// run Axis logics
xAxis.refresh()
yAxis.refresh()
// run series logic
for (serie in series) {
serie.refresh()
}
// invalidate the view
invalidate()
// removeCallbacks(animator)
// post(animator)
}
override fun onDraw(canvas: Canvas) {
// don't draw anything if everything is empty
if (series.isEmpty() || series.maxOf { it.entries.size } == 0) {
super.onDraw(canvas)
return
}
if (debug) {
// draw corners
canvas.drawRect(rect.apply {
set(
padding / 2,
padding / 2,
width.toFloat() - padding / 2,
height.toFloat() - padding / 2
)
}, debugStrokePaint)
}
val bottom = xAxis.onDraw(canvas, rect.apply {
set(padding, 0f, width.toFloat() - padding, height.toFloat() - padding)
})
// right distance from the yAxis
val rightDistance = yAxis.onDraw(canvas, rect.apply {
set(padding, padding, width.toFloat() - padding, height.toFloat() - bottom - padding)
})
// chart draw rectangle
rect.apply {
set(
padding,
padding,
width.toFloat() - padding - rightDistance,
height - bottom - padding
)
}
for (serie in series) {
serie.onDraw(canvas, rect)
}
super.onDraw(canvas)
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent): Boolean {
performClick()
return scroller.onTouchEvent(event)
}
}

View File

@ -1,166 +0,0 @@
package com.dzeio.charts
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.util.AttributeSet
import android.util.Log
import android.view.MotionEvent
import android.view.View
import com.dzeio.charts.axis.XAxis
import com.dzeio.charts.axis.YAxis
import com.dzeio.charts.components.ChartScroll
import com.dzeio.charts.series.SerieAbstract
import kotlin.math.max
import kotlin.math.min
class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet? = null) :
View(context, attrs) {
companion object {
const val TAG = "DzeioCharts/ChartView"
}
override var debug = false
override val xAxis = XAxis<Float>()
override val yAxis = YAxis(this)
override val animation = Animation()
override val scroller = ChartScroll(this).apply {
setOnChartMoved { movementX, _ ->
// Log.d(TAG, "scrolled: $movementX")
movementOffset = movementX / 100
refresh()
}
setOnZoomChanged {
Log.d(TAG, "New Zoom: $it")
zoom = (it * 1.2).toFloat()
refresh()
}
}
override val animator: Runnable = object : Runnable {
override fun run() {
var needNewFrame = false
for (serie in series) {
val result = serie.onUpdate()
if (result) {
needNewFrame = true
}
}
if (needNewFrame) {
postDelayed(this, animation.getDelay().toLong())
}
invalidate()
}
}
/**
* global padding
*/
override var padding: Float = 8f
override var series: ArrayList<SerieAbstract> = arrayListOf()
set(value) {
for (serie in value) {
serie.view = this
}
field = value
}
/**
* Number of entries displayed at the same time
*/
override var zoom = 100f
override var movementOffset: Float = 0f
override val rectF = RectF()
override val otherUseRectF = RectF()
override fun refresh() {
for (serie in series) {
serie.prepareData()
}
rectF.set(
padding,
padding,
measuredWidth - padding - yAxis.getWidth() - padding,
height - padding
)
removeCallbacks(animator)
post(animator)
}
override val fgPaint: Paint = Paint().also {
it.isAntiAlias = true
it.color = Color.parseColor("#123456")
}
override fun onDraw(canvas: Canvas) {
if (yAxis.legendEnabled) {
yAxis.display(canvas, measuredWidth, height)
}
for (serie in series) {
serie.displayData(canvas, rectF)
}
// canvas.drawRect(
// measuredWidth - padding - yAxis.getWidth(),
// 0f,
// measuredWidth - padding,
// height - padding,
// fgPaint
// )
super.onDraw(canvas)
}
override fun onTouchEvent(event: MotionEvent): Boolean {
performClick()
return scroller.onTouchEvent(event)
}
override fun getXOffset(): Int {
// Log.d(
// TAG,
// "baseOffset: ${xAxis.baseOffset}, mOffset: $movementOffset = ${xAxis.baseOffset + movementOffset}"
// )
// Log.d(
// TAG,
// "longestOffset: ${longestSerie()}, displayedEntries: ${getDisplayedEntries()} = ${longestSerie() - getDisplayedEntries()}"
// )
return min(
max(0f, xAxis.baseOffset + movementOffset).toInt(),
max(0, getCalculatedMax() - getDisplayedEntries())
)
}
override fun getDisplayedEntries(): Int {
// Log.d(TAG, "Number of entries displayed ${list.size}, ${xAxis.entriesDisplayed} + (($zoom - 100) * 10) = ${xAxis.entriesDisplayed + ((zoom - 100) * 10).toInt()}")
return max(0, xAxis.entriesDisplayed + ((zoom - 100) * 10).toInt())
}
override fun getCalculatedMax(): Int {
var size = 0
for (serie in series) {
if (serie.datas.size > size) size = serie.datas.size
}
return size
}
override fun getXMax(displayedOnly: Boolean): Float {
var max = 0f
for (serie in series) {
val res = serie.getYMax(displayedOnly)
if (max < res) max = res
}
return max
}
}

View File

@ -13,6 +13,11 @@ interface ChartViewInterface {
*/
var debug: Boolean
/**
* the padding inside the view
*/
var padding: Float
/**
* Hold metadata about the X axis
*/
@ -29,7 +34,9 @@ interface ChartViewInterface {
var series: ArrayList<SerieInterface>
/**
* refresh the chart
* refresh and run pre-display logic the chart
*
* this function should be run if you change parameters in the view
*/
fun refresh()
}

View File

@ -1,5 +1,8 @@
package com.dzeio.charts
/**
* A Base entry for any charts
*/
data class Entry(
val x: Double,
val y: Float

View File

@ -1,5 +1,9 @@
package com.dzeio.charts.axis
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import com.dzeio.charts.ChartViewInterface
import com.dzeio.charts.Entry
@ -23,9 +27,20 @@ class XAxis(
field = value
}
override var enabled = true
override var increment: Double = 1.0
override var displayCount: Int = 10
override var labelCount: Int = 3
override var labelCount: Int = 2
override val textPaint = Paint().apply {
isAntiAlias = true
color = Color.parseColor("#FC496D")
textSize = 30f
textAlign = Paint.Align.LEFT
}
private val rect = Rect()
override fun getPositionOnRect(entry: Entry, rect: RectF): Double {
return translatePositionToRect(entry.x, rect)
@ -52,5 +67,40 @@ class XAxis(
}
}
var onValueFormat: (value: Double) -> String = { it -> it.toString() }
override fun onDraw(canvas: Canvas, space: RectF): Float {
if (!enabled) {
return 0f
}
var maxHeight = 0f
val graphIncrement = space.width() / (labelCount - 1)
val valueIncrement = (displayCount * increment / (labelCount - 1)).toDouble()
for (index in 0 until labelCount) {
val text = onValueFormat(x + valueIncrement * index)
textPaint.getTextBounds(text, 0, text.length, rect)
maxHeight = maxHeight.coerceAtLeast(rect.height().toFloat() + 1)
var xPos = space.left + graphIncrement * index
if (xPos + rect.width() > space.right) {
xPos = space.right - rect.width()
}
canvas.drawText(
text,
xPos,
space.bottom,
textPaint
)
}
return maxHeight + 32f
}
override fun refresh() {
TODO("Not yet implemented")
}
}

View File

@ -1,25 +0,0 @@
package com.dzeio.charts.axis
import com.dzeio.charts.XAxisLabels
class XAxis<T> {
var max: T? = null
var min: T? = null
val labels = XAxisLabels()
/**
* Number of entries displayed in the chart at the same time
*/
var entriesDisplayed = 5
/**
* Offset in the list
*/
var baseOffset = 0
var onValueFormat: (it: T) -> String = onValueFormat@{
return@onValueFormat it.toString()
}
}

View File

@ -1,10 +1,17 @@
package com.dzeio.charts.axis
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
import com.dzeio.charts.Entry
sealed interface XAxisInterface {
/**
* enable/disable the display of the xAxis
*/
var enabled: Boolean
/**
* set X position
*/
@ -15,6 +22,11 @@ sealed interface XAxisInterface {
*/
var increment: Double
/**
* text Paint
*/
val textPaint: Paint
/**
* indicate the max number of entries are displayed
*/
@ -25,11 +37,42 @@ sealed interface XAxisInterface {
*/
var labelCount: Int
/**
* run when manually refreshing the system
*
* this is where the pre-logic is handled to make [onDraw] quicker
*/
fun refresh()
/**
* get the entry position on the rect
*
* @return the left side of the position of the entry
*/
fun getPositionOnRect(entry: Entry, rect: RectF): Double
/**
* get the graph offset for X (kinda like [getPositionOnRect])
*/
fun getXOffset(rect: RectF): Double
/**
* get the maximum the X can get to
*/
fun getXMax(): Double
/**
* get the minimum the X can get to
*/
fun getXMin(): Double
/**
* onDraw event that will draw the XAxis
*
* @param canvas the canvas to draw on
* @param space the space where it is allowed to draw
*
* @return the final height of the XAxis
*/
fun onDraw(canvas: Canvas, space: RectF): Float
}

View File

@ -4,8 +4,8 @@ import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import com.dzeio.charts.ChartViewInterface
import com.dzeio.charts.utils.drawDottedLine
class YAxis(
private val view: ChartViewInterface
@ -25,11 +25,11 @@ class YAxis(
color = Color.BLUE
}
var onValueFormat: (value: Float) -> String = { it -> it.toString()}
var onValueFormat: (value: Float) -> String = { it -> it.toString() }
override var labelCount: Int = 3
override var labelCount = 5
private var min: Float? = null
private var min: Float? = 0f
private var max: Float? = null
private val rect = Rect()
@ -76,14 +76,14 @@ class YAxis(
}
}
override fun onDraw(canvas: Canvas): Float {
override fun onDraw(canvas: Canvas, space: RectF): Float {
if (!enabled) {
return 0f
}
val min = getYMin()
val max = getYMax() - min
val top = 0
val bottom = canvas.height.toFloat()
val top = space.top
val bottom = space.bottom
var maxWidth = 0f
val increment = (bottom - top) / labelCount
@ -97,13 +97,18 @@ class YAxis(
canvas.drawText(
text,
canvas.width - rect.width().toFloat(),
posY + rect.height() + 8f,
space.width() - rect.width().toFloat(),
(posY + rect.height() / 2).coerceAtLeast(rect.height().toFloat()),
textLabel
)
canvas.drawDottedLine(0f, posY, canvas.width.toFloat(), posY, 40f, linePaint)
// canvas.drawDottedLine(0f, posY, canvas.width.toFloat(), posY, 40f, linePaint)
canvas.drawLine(space.left, posY, space.right - maxWidth - 32f, posY, linePaint)
}
return maxWidth
return maxWidth + 32f
}
}
override fun refresh() {
TODO("Not yet implemented")
}
}

View File

@ -1,74 +0,0 @@
package com.dzeio.charts.axis
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import androidx.annotation.ColorInt
import com.dzeio.charts.ChartView
import com.dzeio.charts.utils.drawDottedLine
class YAxis<T>(
private val chartView: ChartView
) {
var max: T? = null
var min: T? = null
/**
* Number of labels displayed on the sidebar
*/
var labelCount: Int = 4
@ColorInt
var color = Color.parseColor("#FC496D")
var textPaint: Paint = Paint().also {
it.isAntiAlias = true
it.color = color
it.textSize = 30f
it.textAlign = Paint.Align.RIGHT
}
var linePaint = Paint().apply {
isAntiAlias = true
}
var legendEnabled = true
private val rect: Rect = Rect()
fun getWidth(): Int {
var maxWidth = 0
val max = chartView.getXMax(true)
val vIncrement = max / labelCount
for (i in 0 until labelCount) {
val text = onValueFormat(vIncrement * (labelCount - i), true)
textPaint.getTextBounds(text, 0, text.length, rect)
if (rect.width() > maxWidth) maxWidth = rect.width()
}
return maxWidth + chartView.padding.toInt()
}
/**
* Function to display the YAxis sidebar on the right
*
* it migh also display content over the Graph
*/
fun display(canvas: Canvas, width: Int, height: Int) {
val max = chartView.getXMax(true)
val increment = (height - chartView.padding * 2) / labelCount
val vIncrement = max / labelCount
for (i in 0 until labelCount) {
val text = onValueFormat(vIncrement * (labelCount - i), true)
textPaint.getTextBounds(text, 0, text.length, rect)
val posY = increment * i.toFloat()
canvas.drawDottedLine(0f, posY + chartView.padding, width - rect.width().toFloat() - chartView.padding, posY, 40f, linePaint)
canvas.drawText(text,
width.toFloat() - chartView.padding, posY + chartView.padding, textPaint)
// canvas.drawDottedLine(0f, posY, measuredWidth.toFloat(), posY, 10f, linePaint)
}
}
var onValueFormat: (value: Float, shortVersion: Boolean) -> String = { it, _ -> it.toString() }
}

View File

@ -2,6 +2,7 @@ package com.dzeio.charts.axis
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.RectF
sealed interface YAxisInterface {
@ -10,6 +11,30 @@ sealed interface YAxisInterface {
*/
var enabled: Boolean
/**
* get/set the number of label of this Y axis
*
* the first/last labels are at the bottom/top of the chart
*/
var labelCount: Int
/**
* text label paint
*/
val textLabel: Paint
/**
* paint for the lines
*/
val linePaint: Paint
/**
* run when manually refreshing the system
*
* this is where the pre-logic is handled to make [onDraw] quicker
*/
fun refresh()
/**
* override Y minimum
*
@ -38,27 +63,13 @@ sealed interface YAxisInterface {
*/
fun getYMin(): Float
/**
* get/set the number of label of this Y axis
*
* the first/last labels are at the bottom/top of the chart
*/
var labelCount: Int
/**
* text label paint
*/
val textLabel: Paint
/**
* paint for the dotted line
*/
val linePaint: Paint
/**
* function that draw our legend
*
* @param canvas the canvas to draw on
* @param space the space where it is allowed to draw on
*
* @return the width of the sidebar
*/
fun onDraw(canvas: Canvas): Float
fun onDraw(canvas: Canvas, space: RectF): Float
}

View File

@ -1,5 +1,10 @@
package com.dzeio.charts.axis
/**
* CURRENTLY UNUSED
*
* declare where the YAxis for the graph will be
*/
enum class YAxisPosition {
LEFT,
RIGHT

View File

@ -5,6 +5,9 @@ import android.view.MotionEvent.INVALID_POINTER_ID
import android.view.ScaleGestureDetector
import android.view.View
/**
* Class handling the scroll/zoom for the library
*/
class ChartScroll(view: View) {
/**

View File

@ -1,6 +1,10 @@
package com.dzeio.charts.series
import android.graphics.*
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.graphics.RectF
import android.util.Log
import com.dzeio.charts.ChartView
import com.dzeio.charts.utils.drawRoundRect
@ -13,6 +17,10 @@ class BarSerie(
const val TAG = "Charts/BarSerie"
}
init {
view.series.add(this)
}
val barPaint = Paint().apply {
isAntiAlias = true
color = Color.parseColor("#123456")
@ -38,8 +46,8 @@ class BarSerie(
for (entry in displayedEntries) {
// calculated height in percent from 0 to 100
val top = (1 - entry.y / max) * drawableSpace.height()
val posX = (view.xAxis.getPositionOnRect(entry, drawableSpace) - view.xAxis.getXOffset(drawableSpace)).toFloat()
val top = (1 - entry.y / max) * drawableSpace.height() + drawableSpace.top
var posX = drawableSpace.left + (view.xAxis.getPositionOnRect(entry, drawableSpace) - view.xAxis.getXOffset(drawableSpace)).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)}")
@ -48,14 +56,20 @@ class BarSerie(
TAG, """
${posX},
$top,
${(posX + barWidth).toFloat()},
${(posX + barWidth)},
${drawableSpace.bottom}""".trimIndent()
)
val right = (posX + barWidth).toFloat().coerceAtMost(drawableSpace.right)
val right = (posX + barWidth).coerceAtMost(drawableSpace.right)
if (posX > right) {
continue
} else if (posX < drawableSpace.left) {
posX = drawableSpace.left
}
if (right < drawableSpace.left) {
continue
}
canvas.drawRoundRect(
@ -76,9 +90,14 @@ class BarSerie(
textPaint.getTextBounds(text, 0, text.length, rect)
val textLeft = (posX + barWidth / 2).toFloat()
val textLeft = (posX + barWidth / 2)
if (textLeft + rect.width() / 2 > right) {
if (
// handle right side
textLeft + rect.width() / 2 > right ||
// handle left sie
textLeft - rect.width() / 2 < drawableSpace.left
) {
continue
}
@ -86,14 +105,23 @@ class BarSerie(
rect.height() + 40f < drawableSpace.bottom - top &&
rect.width() < barWidth
var textY = if (doDisplayIn) top + rect.height() + 20f else top - 20f
if (textY < 0) {
textY = drawableSpace.top + rect.height()
}
canvas.drawText(
text,
textLeft,
if (doDisplayIn) top + rect.height() + 20f else top - 20f,
textY,
textPaint
)
}
}
override fun refresh() {
TODO("Not yet implemented")
}
}

View File

@ -1,64 +0,0 @@
package com.dzeio.charts.series
import android.graphics.Canvas
import android.graphics.RectF
import com.dzeio.charts.ChartView
import com.dzeio.charts.Entry
import kotlin.math.min
abstract class SerieAbstract {
var datas: ArrayList<Entry> = arrayListOf()
lateinit var view: ChartView
/**
* get Serie Y max
*/
fun getYMax(displayedOnly: Boolean = false): Float {
var max = Float.MIN_VALUE
val localDatas = if (displayedOnly) getDisplayedEntries() else datas
for (data in localDatas) {
if (max < data.y) max = data.y
}
return max
}
/**
* get Serie Y min
*/
fun getYMin(displayedOnly: Boolean = false): Float {
var min = Float.MAX_VALUE
val localDatas = if (displayedOnly) getDisplayedEntries() else datas
for (data in localDatas) {
if (min > data.y) min = data.y
}
return min
}
/**
* Animation updates
*/
abstract fun onUpdate(): Boolean
/**
* Function to prepare for an update
*/
abstract fun prepareData()
/**
* Function to display data on the graph
*
* @param canvas the canvas to draw on
* @param rect the rectangle in which you have to draw data
*/
abstract fun displayData(canvas: Canvas, rect: RectF)
protected fun getDisplayedEntries(): MutableList<Entry> {
return datas.subList(
view.getXOffset(),
min(datas.size, view.getDisplayedEntries() + view.getXOffset())
)
}
}

View File

@ -24,5 +24,18 @@ sealed interface SerieInterface {
*/
var entries: ArrayList<Entry>
/**
* function that display the graph
*
* @param canvas the canvas to draw on
* @param drawableSpace the space you are allowed to draw on
*/
fun onDraw(canvas: Canvas, drawableSpace: RectF)
/**
* run when manually refreshing the system
*
* this is where the pre-logic is handled to make [onDraw] quicker
*/
fun refresh()
}

View File

@ -5,6 +5,9 @@ import android.graphics.Paint
import android.graphics.RectF
import kotlin.math.sqrt
/**
* draw a dotted line
*/
fun Canvas.drawDottedLine(
startX: Float,
startY: Float,