mirror of
https://github.com/dzeiocom/charts.git
synced 2025-04-22 10:42:09 +00:00
feat: Add basic Stacked charts support (#22)
This commit is contained in:
parent
6afe0a938e
commit
ed06aa697e
@ -144,8 +144,14 @@ class ChartView @JvmOverloads constructor(context: Context?, attrs: AttributeSet
|
||||
)
|
||||
}
|
||||
|
||||
for (serie in series) {
|
||||
serie.onDraw(canvas, rect)
|
||||
if (type == ChartType.STACKED) {
|
||||
for (serie in series.reversed()) {
|
||||
serie.onDraw(canvas, rect)
|
||||
}
|
||||
} else {
|
||||
for (serie in series) {
|
||||
serie.onDraw(canvas, rect)
|
||||
}
|
||||
}
|
||||
super.onDraw(canvas)
|
||||
}
|
||||
|
@ -5,9 +5,12 @@ import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Rect
|
||||
import android.graphics.RectF
|
||||
import com.dzeio.charts.ChartType
|
||||
import com.dzeio.charts.ChartViewInterface
|
||||
import com.dzeio.charts.Entry
|
||||
import com.dzeio.charts.utils.drawDottedLine
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.math.sign
|
||||
|
||||
class YAxis(
|
||||
private val view: ChartViewInterface
|
||||
@ -37,7 +40,7 @@ class YAxis(
|
||||
|
||||
override var labelCount = 5
|
||||
|
||||
private var min: Float? = 0f
|
||||
private var min: Float? = null
|
||||
private var max: Float? = null
|
||||
|
||||
@Deprecated("use the new global function", replaceWith = ReplaceWith("YAxisInterface.addLine"))
|
||||
@ -68,6 +71,22 @@ class YAxis(
|
||||
if (view.series.isEmpty()) {
|
||||
return this.lines.keys.maxOrNull() ?: 0f
|
||||
}
|
||||
if (view.type == ChartType.STACKED) {
|
||||
val nList: ArrayList<Float> = arrayListOf()
|
||||
|
||||
for (serie in view.series) {
|
||||
val size = serie.entries.size
|
||||
while (nList.size < size) {
|
||||
nList.add(0f)
|
||||
}
|
||||
for (index in 0 until serie.entries.size) {
|
||||
val entry = serie.entries[index]
|
||||
nList[index] += entry.y
|
||||
}
|
||||
}
|
||||
|
||||
return nList.maxOf { it }
|
||||
}
|
||||
val seriesMax = view.series
|
||||
.maxOf { serie ->
|
||||
if (serie.getDisplayedEntries().isEmpty()) {
|
||||
@ -85,6 +104,22 @@ class YAxis(
|
||||
if (view.series.isEmpty()) {
|
||||
return this.lines.keys.minOrNull() ?: 0f
|
||||
}
|
||||
if (view.type == ChartType.STACKED) {
|
||||
val nList: ArrayList<Float> = arrayListOf()
|
||||
|
||||
for (serie in view.series) {
|
||||
val size = serie.entries.size
|
||||
while (nList.size < size) {
|
||||
nList.add(0f)
|
||||
}
|
||||
for (index in 0 until serie.entries.size) {
|
||||
val entry = serie.entries[index]
|
||||
nList[index] += entry.y
|
||||
}
|
||||
}
|
||||
|
||||
return nList.minOf { it }
|
||||
}
|
||||
return view.series
|
||||
.minOf { serie ->
|
||||
if (serie.getDisplayedEntries().isEmpty()) {
|
||||
@ -168,4 +203,35 @@ class YAxis(
|
||||
addLine(height, Line(true))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Float {
|
||||
if (view.type == ChartType.STACKED) {
|
||||
val serie = view.series.find { it.entries.contains(entry) }
|
||||
val index = view.series.indexOf(serie)
|
||||
return getPositionOnRect(entry, drawableSpace, index)
|
||||
}
|
||||
return getPositionOnRect(entry.y, drawableSpace)
|
||||
}
|
||||
|
||||
private fun getPositionOnRect(entry: Entry, drawableSpace: RectF, index: Int): Float {
|
||||
if (index > 0) {
|
||||
val entry2 = view.series[index - 1].entries.find { it.x == entry.x }
|
||||
if (entry2 != null) {
|
||||
// make a new """Entry""" containing the new Y
|
||||
val isReverse = sign(entry2.y) != sign(entry.y)
|
||||
val tmp = Entry(entry.x, if (isReverse) entry.y else entry.y + entry2.y)
|
||||
return getPositionOnRect(tmp, drawableSpace, index - 1)
|
||||
}
|
||||
}
|
||||
return getPositionOnRect(entry.y, drawableSpace)
|
||||
}
|
||||
|
||||
override fun getPositionOnRect(point: Float, drawableSpace: RectF): Float {
|
||||
val min = getYMin()
|
||||
val max = getYMax()
|
||||
|
||||
return (1 - (point - min) / (max - min)) *
|
||||
drawableSpace.height() +
|
||||
drawableSpace.top
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package com.dzeio.charts.axis
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Paint
|
||||
import android.graphics.RectF
|
||||
import com.dzeio.charts.Entry
|
||||
|
||||
sealed interface YAxisInterface {
|
||||
|
||||
@ -110,4 +111,24 @@ sealed interface YAxisInterface {
|
||||
* @param y the Y position of the line
|
||||
*/
|
||||
fun removeLine(y: Float)
|
||||
|
||||
/**
|
||||
* get the position of an [entry] Y position in the [drawableSpace]
|
||||
*
|
||||
* if the chart type is stacked it will automatically calculate the position depending on it
|
||||
*
|
||||
* @param entry the entry to search to position
|
||||
* @param drawableSpace the space in which it should appear
|
||||
* @return the float position (can be out of the [drawableSpace])
|
||||
*/
|
||||
fun getPositionOnRect(entry: Entry, drawableSpace: RectF): Float
|
||||
|
||||
/**
|
||||
* get the position of a [point] in the [drawableSpace]
|
||||
*
|
||||
* @param point the point to search to position
|
||||
* @param drawableSpace the space in which it should appear
|
||||
* @return the float position (can be out of the [drawableSpace])
|
||||
*/
|
||||
fun getPositionOnRect(point: Float, drawableSpace: RectF): Float
|
||||
}
|
||||
|
@ -44,17 +44,12 @@ class BarSerie(
|
||||
override fun onDraw(canvas: Canvas, drawableSpace: RectF) {
|
||||
val displayedEntries = getDisplayedEntries()
|
||||
val barWidth = view.xAxis.getEntryWidth(drawableSpace).toFloat()
|
||||
val max = view.yAxis.getYMax()
|
||||
val min = view.yAxis.getYMin()
|
||||
|
||||
val zero = ((1 - -min / (max - min)) * drawableSpace.height() + drawableSpace.top).coerceIn(
|
||||
drawableSpace.top, drawableSpace.bottom
|
||||
)
|
||||
val zero = view.yAxis.getPositionOnRect(0f, drawableSpace)
|
||||
|
||||
for (entry in displayedEntries) {
|
||||
// calculated height in percent from 0 to 100
|
||||
val top = ((1 - (entry.y - min) / (max - min)) * drawableSpace.height() + drawableSpace.top)
|
||||
.coerceIn(drawableSpace.top, drawableSpace.bottom)
|
||||
val top = view.yAxis.getPositionOnRect(entry, drawableSpace)
|
||||
var posX = drawableSpace.left + view.xAxis.getPositionOnRect(
|
||||
entry,
|
||||
drawableSpace
|
||||
|
@ -34,15 +34,13 @@ class LineSerie(
|
||||
override fun onDraw(canvas: Canvas, drawableSpace: RectF) {
|
||||
val displayedEntries = getDisplayedEntries()
|
||||
displayedEntries.sortBy { it.x }
|
||||
val max = view.yAxis.getYMax()
|
||||
val min = view.yAxis.getYMin()
|
||||
|
||||
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 - min) / (max - min)) * drawableSpace.height() + drawableSpace.top
|
||||
val top = view.yAxis.getPositionOnRect(entry, drawableSpace)
|
||||
val posX = (drawableSpace.left +
|
||||
view.xAxis.getPositionOnRect(entry, drawableSpace) +
|
||||
view.xAxis.getEntryWidth(drawableSpace) / 2f).toFloat()
|
||||
|
@ -37,15 +37,15 @@ class MainFragment : Fragment() {
|
||||
val serie2 = BarSerie(this)
|
||||
|
||||
// transform the chart into a grouped chart
|
||||
type = ChartType.GROUPED
|
||||
type = ChartType.STACKED
|
||||
|
||||
// utils function to use Material3 auto colors
|
||||
materielTheme(this, requireView())
|
||||
serie2.barPaint.color = Color.RED
|
||||
|
||||
// give the serie it's entries
|
||||
serie1.entries = generateRandomDataset(10)
|
||||
serie2.entries = generateRandomDataset(10)
|
||||
serie1.entries = generateRandomDataset(1)
|
||||
serie2.entries = generateRandomDataset(1)
|
||||
|
||||
// refresh the Chart
|
||||
refresh()
|
||||
@ -164,7 +164,7 @@ class MainFragment : Fragment() {
|
||||
private fun generateRandomDataset(size: Int = 100, min: Int = 0, max: Int = 100): ArrayList<Entry> {
|
||||
val dataset: ArrayList<Entry> = arrayListOf()
|
||||
|
||||
for (i in 0 .. size) {
|
||||
for (i in 0 until size) {
|
||||
dataset.add(Entry(
|
||||
i.toDouble(),
|
||||
Random.nextInt(min, max).toFloat()
|
||||
@ -233,4 +233,4 @@ class MainFragment : Fragment() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user