feat: add line chart composable
This commit is contained in:
parent
bd4c5ea0f3
commit
27bc8f4100
|
|
@ -0,0 +1,135 @@
|
|||
package com.syaroful.agrilinkvocpro.ui.screen.detail
|
||||
|
||||
import android.graphics.Paint
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.asAndroidPath
|
||||
import androidx.compose.ui.graphics.asComposePath
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.nativeCanvas
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.syaroful.agrilinkvocpro.ui.theme.MainGreen
|
||||
import kotlin.math.round
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@Composable
|
||||
fun LineChart(
|
||||
hours: List<Int> = emptyList(),
|
||||
values: List<Double> = emptyList(),
|
||||
modifier: Modifier = Modifier,
|
||||
graphColor: Color = MainGreen
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.background(color = MaterialTheme.colorScheme.surfaceContainer, shape = RoundedCornerShape(8.dp))
|
||||
.padding(top = 20.dp)
|
||||
.padding(8.dp)
|
||||
) {
|
||||
val spacing = 100f
|
||||
val transparentGraphColor = remember {
|
||||
graphColor.copy(alpha = 0.5f)
|
||||
}
|
||||
val upperValue = remember(values) {
|
||||
(values.maxOrNull()?.plus(1))?.roundToInt() ?: 0
|
||||
}
|
||||
val lowerValue = remember(values) {
|
||||
values.minOrNull()?.toInt() ?: 0
|
||||
}
|
||||
val density = LocalDensity.current
|
||||
val isDarkMode = isSystemInDarkTheme()
|
||||
val textPaint = remember(density) {
|
||||
Paint().apply {
|
||||
color =
|
||||
if (isDarkMode) android.graphics.Color.WHITE else android.graphics.Color.BLACK
|
||||
textAlign = Paint.Align.CENTER
|
||||
textSize = density.run { 12.sp.toPx() }
|
||||
}
|
||||
}
|
||||
|
||||
Canvas(modifier = modifier) {
|
||||
val spacePerHour = (size.width - spacing) / values.size
|
||||
(0 until hours.size - 1 step 2).forEach { i ->
|
||||
val value = values[i]
|
||||
val hour = hours[i]
|
||||
drawContext.canvas.nativeCanvas.apply {
|
||||
drawText(
|
||||
hour.toString(),
|
||||
spacing + i * spacePerHour,
|
||||
size.height - 5,
|
||||
textPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
val priceStep = (upperValue - lowerValue) / 5f
|
||||
(0..4).forEach { i ->
|
||||
drawContext.canvas.nativeCanvas.apply {
|
||||
drawText(
|
||||
round(lowerValue + priceStep * i).toString(),
|
||||
30f,
|
||||
size.height - spacing - i * size.height / 5f,
|
||||
textPaint
|
||||
)
|
||||
}
|
||||
}
|
||||
var lastX = 0f
|
||||
val strokePath = Path().apply {
|
||||
val height = size.height
|
||||
for (i in values.indices) {
|
||||
val value = values[i]
|
||||
val nextValue = values.getOrNull(i + 1) ?: values.last()
|
||||
val leftRatio = (value - lowerValue) / (upperValue - lowerValue)
|
||||
val rightRatio = (nextValue - lowerValue) / (upperValue - lowerValue)
|
||||
|
||||
val x1 = spacing + i * spacePerHour
|
||||
val y1 = height - spacing - (leftRatio * height).toFloat()
|
||||
val x2 = spacing + (i + 1) * spacePerHour
|
||||
val y2 = height - spacing - (rightRatio * height).toFloat()
|
||||
if (i == 0) {
|
||||
moveTo(x1, y1)
|
||||
}
|
||||
lastX = (x1 + x2) / 2f
|
||||
quadraticTo(x1, y1, lastX, (y1 + y2) / 2f)
|
||||
}
|
||||
}
|
||||
val fillPath = android.graphics.Path(strokePath.asAndroidPath())
|
||||
.asComposePath()
|
||||
.apply {
|
||||
lineTo(lastX, size.height - spacing)
|
||||
lineTo(spacing, size.height - spacing)
|
||||
close()
|
||||
}
|
||||
drawPath(
|
||||
path = fillPath,
|
||||
brush = Brush.verticalGradient(
|
||||
colors = listOf(
|
||||
transparentGraphColor,
|
||||
Color.Transparent
|
||||
),
|
||||
endY = size.height - spacing
|
||||
)
|
||||
)
|
||||
drawPath(
|
||||
path = strokePath,
|
||||
color = graphColor,
|
||||
style = Stroke(
|
||||
width = 3.dp.toPx(),
|
||||
cap = StrokeCap.Round
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user