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:
parent
07d150968e
commit
39f6e7cbef
@ -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
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
package com.dzeio.charts
|
||||
|
||||
/**
|
||||
* A Base entry for any charts
|
||||
*/
|
||||
data class Entry(
|
||||
val x: Double,
|
||||
val y: Float
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
@ -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() }
|
||||
|
||||
}
|
@ -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
|
||||
}
|
@ -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
|
||||
|
@ -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) {
|
||||
|
||||
/**
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
@ -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())
|
||||
)
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user