mirror of
https://github.com/dzeiocom/OpenHealth.git
synced 2025-07-29 12:49:50 +00:00
feat: Change inner work to allow more depictions of datas
This commit is contained in:
@ -34,9 +34,7 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
||||
private val scroller = ChartScroll(this).apply {
|
||||
var lastMovement = 0.0
|
||||
setOnChartMoved { movementX, _ ->
|
||||
|
||||
// Log.d(TAG, "scrolled: ${(movementX - lastMovement) * (xAxis.increment / 10)}")
|
||||
xAxis.x = xAxis.x + (movementX - lastMovement) * (xAxis.increment * xAxis.displayCount / width)
|
||||
xAxis.x += (movementX - lastMovement) * xAxis.getDataWidth() / width
|
||||
lastMovement = movementX.toDouble()
|
||||
refresh()
|
||||
}
|
||||
@ -140,4 +138,13 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
||||
performClick()
|
||||
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
|
||||
*/
|
||||
fun refresh()
|
||||
|
||||
/**
|
||||
* @return the whole dataset (sorted and cleaned up of dupps)
|
||||
*/
|
||||
fun getDataset(): ArrayList<Entry>
|
||||
}
|
@ -13,7 +13,7 @@ class XAxis(
|
||||
) : XAxisInterface {
|
||||
override var x: Double = 0.0
|
||||
set(value) {
|
||||
val max = getXMax() - increment * displayCount
|
||||
val max = getXMax() - getDataWidth()
|
||||
val min = getXMin()
|
||||
if (value > max && min <= max) {
|
||||
field = max
|
||||
@ -29,10 +29,13 @@ class XAxis(
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
var spacing = 16.0
|
||||
|
||||
override val textPaint = Paint().apply {
|
||||
isAntiAlias = true
|
||||
color = Color.parseColor("#FC496D")
|
||||
@ -42,27 +45,28 @@ class XAxis(
|
||||
|
||||
private val rect = Rect()
|
||||
|
||||
override fun getPositionOnRect(entry: Entry, rect: RectF): Double {
|
||||
return translatePositionToRect(entry.x, rect)
|
||||
override fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Double {
|
||||
return translatePositionToRect(entry.x, drawableSpace)
|
||||
}
|
||||
|
||||
fun translatePositionToRect(value: Double, rect: RectF): Double {
|
||||
val rectItem = rect.width() / displayCount // item size in graph
|
||||
return rectItem * value / increment
|
||||
}
|
||||
|
||||
override fun getXOffset(rect: RectF): Double {
|
||||
return translatePositionToRect(x, rect)
|
||||
fun translatePositionToRect(value: Double, drawableSpace: RectF): Double {
|
||||
return drawableSpace.width() * (value - x) / getDataWidth()
|
||||
}
|
||||
|
||||
override fun getXMax(): Double {
|
||||
return view.series.maxOf { serie ->
|
||||
if (serie.entries.isEmpty()) {
|
||||
return 0.0
|
||||
}
|
||||
serie.entries.maxOf { entry -> entry.x }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getXMin(): Double {
|
||||
return view.series.minOf { serie ->
|
||||
if (serie.entries.isEmpty()) {
|
||||
return 0.0
|
||||
}
|
||||
serie.entries.minOf { entry -> entry.x }
|
||||
}
|
||||
}
|
||||
@ -77,7 +81,7 @@ class XAxis(
|
||||
var maxHeight = 0f
|
||||
|
||||
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) {
|
||||
val text = onValueFormat(x + valueIncrement * index)
|
||||
textPaint.getTextBounds(text, 0, text.length, rect)
|
||||
@ -89,7 +93,6 @@ class XAxis(
|
||||
xPos = space.right - rect.width()
|
||||
}
|
||||
|
||||
|
||||
canvas.drawText(
|
||||
text,
|
||||
xPos,
|
||||
@ -103,4 +106,20 @@ class XAxis(
|
||||
override fun refresh() {
|
||||
// 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
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
val textPaint: Paint
|
||||
|
||||
/**
|
||||
* indicate the max number of entries are displayed
|
||||
*/
|
||||
var displayCount: Int
|
||||
|
||||
/**
|
||||
* indicate the number of labels displayed
|
||||
*/
|
||||
@ -49,12 +48,7 @@ sealed interface XAxisInterface {
|
||||
*
|
||||
* @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
|
||||
fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Double
|
||||
|
||||
/**
|
||||
* get the maximum the X can get to
|
||||
@ -66,6 +60,18 @@ sealed interface XAxisInterface {
|
||||
*/
|
||||
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
|
||||
*
|
||||
|
@ -5,6 +5,7 @@ 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
|
||||
|
||||
@ -35,9 +36,8 @@ class BarSerie(
|
||||
private val rect = Rect()
|
||||
|
||||
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 barWidth = view.xAxis.getEntryWidth(drawableSpace).toFloat()
|
||||
val max = view.yAxis.getYMax()
|
||||
val min = view.yAxis.getYMin()
|
||||
|
||||
@ -46,21 +46,10 @@ class BarSerie(
|
||||
for (entry in displayedEntries) {
|
||||
// calculated height in percent from 0 to 100
|
||||
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,
|
||||
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)}")
|
||||
|
||||
// Log.d(
|
||||
// TAG, """
|
||||
// ${posX},
|
||||
// $top,
|
||||
// ${(posX + barWidth)},
|
||||
// ${drawableSpace.bottom}""".trimIndent()
|
||||
// )
|
||||
).toFloat()
|
||||
|
||||
val right = (posX + barWidth).coerceAtMost(drawableSpace.right)
|
||||
|
||||
|
@ -19,14 +19,28 @@ sealed class BaseSerie(
|
||||
override var entries: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
override fun getDisplayedEntries(): ArrayList<Entry> {
|
||||
// -+ view.xAxis.increment = one out of display
|
||||
val minX = view.xAxis.x - view.xAxis.increment
|
||||
val maxX =
|
||||
view.xAxis.x + view.xAxis.displayCount * view.xAxis.increment + view.xAxis.increment
|
||||
val minX = view.xAxis.x
|
||||
val maxX = minX + view.xAxis.getDataWidth()
|
||||
|
||||
return entries.filter {
|
||||
return@filter it.x in minX..maxX
|
||||
} as ArrayList<Entry>
|
||||
val result: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
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)
|
||||
|
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")
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user