-
-
Save patrickmichalik/b2fc0a480d901f4c072357a74fbed7ce to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.graphics.Typeface | |
import androidx.compose.runtime.Composable | |
import androidx.compose.runtime.remember | |
import androidx.compose.ui.tooling.preview.Preview | |
import androidx.compose.ui.unit.dp | |
import com.patrykandpatrick.vico.compose.cartesian.CartesianChartHost | |
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLabelComponent | |
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberAxisLineComponent | |
import com.patrykandpatrick.vico.compose.cartesian.axis.rememberStartAxis | |
import com.patrykandpatrick.vico.compose.cartesian.fullWidth | |
import com.patrykandpatrick.vico.compose.cartesian.layer.rememberColumnCartesianLayer | |
import com.patrykandpatrick.vico.compose.cartesian.rememberCartesianChart | |
import com.patrykandpatrick.vico.compose.cartesian.rememberVicoScrollState | |
import com.patrykandpatrick.vico.compose.common.of | |
import com.patrykandpatrick.vico.core.cartesian.CartesianDrawContext | |
import com.patrykandpatrick.vico.core.cartesian.HorizontalLayout | |
import com.patrykandpatrick.vico.core.cartesian.axis.AxisPosition | |
import com.patrykandpatrick.vico.core.cartesian.axis.HorizontalAxis | |
import com.patrykandpatrick.vico.core.cartesian.data.CartesianChartModelProducer | |
import com.patrykandpatrick.vico.core.cartesian.data.CartesianValueFormatter | |
import com.patrykandpatrick.vico.core.cartesian.data.columnSeries | |
import com.patrykandpatrick.vico.core.common.Dimensions | |
import com.patrykandpatrick.vico.core.common.VerticalPosition | |
import com.patrykandpatrick.vico.core.common.component.LineComponent | |
import com.patrykandpatrick.vico.core.common.component.TextComponent | |
import java.time.LocalTime | |
import java.time.format.DateTimeFormatter | |
import kotlin.random.Random | |
private class BottomAxis( | |
label: TextComponent, | |
private val labelPredicate: CartesianDrawContext.(Float) -> Boolean, | |
line: LineComponent, | |
) : HorizontalAxis<AxisPosition.Horizontal.Bottom>(AxisPosition.Horizontal.Bottom) { | |
init { | |
this.label = label | |
valueFormatter = Companion.valueFormatter | |
axisLine = line | |
guideline | |
} | |
override fun drawBehindChart(context: CartesianDrawContext) { | |
with(context) { | |
val baseCanvasX = | |
(if (isLtr) bounds.left else bounds.right) - horizontalScroll + | |
horizontalDimensions.startPadding * layoutDirectionMultiplier | |
val maxLabelHeight = (bounds.height() - axisThickness / 2).toInt() | |
val labelCanvasY = bounds.top + axisThickness | |
val labelX = buildList { | |
var x = chartValues.minX | |
while (x <= chartValues.maxX) { | |
if (labelPredicate(x)) add(x) | |
x += chartValues.xStep | |
} | |
} | |
labelX.forEachIndexed { index, x -> | |
val text = valueFormatter.format(value = x, chartValues = chartValues, verticalAxisPosition = null) | |
var canvasX = | |
baseCanvasX + | |
(x - chartValues.minX) / chartValues.xStep * | |
horizontalDimensions.xSpacing * | |
layoutDirectionMultiplier | |
if (index == 0 || index == labelX.lastIndex) { | |
val width = label?.getWidth(context = this, text = text, height = maxLabelHeight) ?: 0f | |
canvasX = | |
if (isLtr && index == 0 || !isLtr && index == labelX.lastIndex) { | |
canvasX.coerceAtLeast(bounds.left + width / 2) | |
} else { | |
canvasX.coerceAtMost(bounds.right - width / 2) | |
} | |
} | |
label?.drawText( | |
context = this, | |
text = text, | |
textX = canvasX, | |
textY = labelCanvasY, | |
verticalPosition = VerticalPosition.Bottom, | |
maxTextHeight = maxLabelHeight, | |
rotationDegrees = labelRotationDegrees, | |
) | |
} | |
axisLine?.drawHorizontal( | |
context = context, | |
left = chartBounds.left, | |
right = chartBounds.right, | |
centerY = bounds.top + axisThickness / 2, | |
) | |
} | |
} | |
private companion object { | |
val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("hh:mm a") | |
val valueFormatter = CartesianValueFormatter { x, _, _ -> dateTimeFormatter.format(LocalTime.of(x.toInt(), 0)) } | |
} | |
} | |
@Composable | |
private fun rememberBottomAxis(labelPredicate: CartesianDrawContext.(Float) -> Boolean): BottomAxis { | |
val label = rememberAxisLabelComponent(padding = Dimensions.of(vertical = 2.dp), typeface = Typeface.DEFAULT) | |
val line = rememberAxisLineComponent() | |
return remember(label, line) { BottomAxis(label, labelPredicate, line) } | |
} | |
@Preview(showBackground = true) | |
@Composable | |
private fun Example() { | |
val modelProducer = remember { | |
CartesianChartModelProducer.build { | |
columnSeries { series(x = List(48) { it / 2f }, y = List(48) { (Random.nextFloat() + 1) / 2 }) } | |
} | |
} | |
CartesianChartHost( | |
chart = | |
rememberCartesianChart( | |
rememberColumnCartesianLayer(spacing = 2.dp), | |
startAxis = | |
rememberStartAxis(label = rememberAxisLabelComponent(typeface = Typeface.DEFAULT), tick = null), | |
bottomAxis = rememberBottomAxis { it % 6f == 0f || it == 23f }, | |
), | |
modelProducer = modelProducer, | |
scrollState = rememberVicoScrollState(scrollEnabled = false), | |
horizontalLayout = HorizontalLayout.fullWidth(scalableStartPadding = 2.dp, scalableEndPadding = 2.dp), | |
) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment