Replies: 2 comments
-
Bumping the thread. |
Beta Was this translation helpful? Give feedback.
0 replies
-
Hello, @PedroFrr. This requires an override of import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.patrykandpatrick.vico.core.cartesian.CartesianDrawingContext
import com.patrykandpatrick.vico.core.cartesian.marker.CandlestickCartesianLayerMarkerTarget
import com.patrykandpatrick.vico.core.cartesian.marker.CartesianMarker
import com.patrykandpatrick.vico.core.cartesian.marker.ColumnCartesianLayerMarkerTarget
import com.patrykandpatrick.vico.core.cartesian.marker.DefaultCartesianMarker
import com.patrykandpatrick.vico.core.cartesian.marker.LineCartesianLayerMarkerTarget
import com.patrykandpatrick.vico.core.common.Defaults
import com.patrykandpatrick.vico.core.common.Position
import com.patrykandpatrick.vico.core.common.component.Component
import com.patrykandpatrick.vico.core.common.component.LineComponent
import com.patrykandpatrick.vico.core.common.component.TextComponent
import com.patrykandpatrick.vico.core.common.half
import com.patrykandpatrick.vico.core.common.shape.MarkerCorneredShape
public class CustomCartesianMarker(
label: TextComponent,
valueFormatter: ValueFormatter = ValueFormatter.Companion.default(),
labelPosition: LabelPosition = LabelPosition.Top,
indicator: ((Int) -> Component)? = null,
indicatorSizeDp: Float = Defaults.MARKER_INDICATOR_SIZE,
guideline: LineComponent? = null,
) :
DefaultCartesianMarker(
label,
valueFormatter,
labelPosition,
indicator,
indicatorSizeDp,
guideline,
) {
override fun drawOverLayers(
context: CartesianDrawingContext,
targets: List<CartesianMarker.Target>,
) {
with(context) {
drawGuideline(targets)
val halfIndicatorSize = indicatorSizeDp.half.pixels
targets.forEach { target ->
when (target) {
is CandlestickCartesianLayerMarkerTarget -> {
drawIndicator(
target.canvasX,
target.openingCanvasY,
target.openingColor,
halfIndicatorSize,
)
drawIndicator(
target.canvasX,
target.closingCanvasY,
target.closingColor,
halfIndicatorSize,
)
drawIndicator(target.canvasX, target.lowCanvasY, target.lowColor, halfIndicatorSize)
drawIndicator(target.canvasX, target.highCanvasY, target.highColor, halfIndicatorSize)
}
is ColumnCartesianLayerMarkerTarget -> {
target.columns.forEach { column ->
drawIndicator(target.canvasX, column.canvasY, column.color, halfIndicatorSize)
}
}
is LineCartesianLayerMarkerTarget -> {
target.points.forEach { point ->
drawIndicator(target.canvasX, point.canvasY, point.color, halfIndicatorSize)
}
}
}
}
drawLabelWithoutOverriddenXPosition(context, targets)
}
}
private fun drawLabelWithoutOverriddenXPosition(
context: CartesianDrawingContext,
targets: List<CartesianMarker.Target>,
): Unit =
with(context) {
val text = valueFormatter.format(context, targets)
val targetX = targets.fold(0f) { sum, element -> sum + element.canvasX } / targets.size
val labelBounds = label.getBounds(context, text, layerBounds.width().toInt())
markerCorneredShape?.tickX = targetX
val tickPosition: MarkerCorneredShape.TickPosition
val y: Float
val verticalPosition: Position.Vertical
when (labelPosition) {
LabelPosition.Top -> {
tickPosition = MarkerCorneredShape.TickPosition.Bottom
y = context.layerBounds.top - tickSizeDp.pixels
verticalPosition = Position.Vertical.Top
}
LabelPosition.Bottom -> {
tickPosition = MarkerCorneredShape.TickPosition.Top
y = context.layerBounds.bottom + tickSizeDp.pixels
verticalPosition = Position.Vertical.Bottom
}
LabelPosition.AroundPoint,
LabelPosition.AbovePoint -> {
val topPointY =
targets.minOf { target ->
when (target) {
is CandlestickCartesianLayerMarkerTarget -> target.highCanvasY
is ColumnCartesianLayerMarkerTarget ->
target.columns.minOf(ColumnCartesianLayerMarkerTarget.Column::canvasY)
is LineCartesianLayerMarkerTarget ->
target.points.minOf(LineCartesianLayerMarkerTarget.Point::canvasY)
else -> error("Unexpected `CartesianMarker.Target` implementation.")
}
}
val flip =
labelPosition == LabelPosition.AroundPoint &&
topPointY - labelBounds.height() - tickSizeDp.pixels < context.layerBounds.top
tickPosition =
if (flip) MarkerCorneredShape.TickPosition.Top
else MarkerCorneredShape.TickPosition.Bottom
y = topPointY + (if (flip) 1 else -1) * tickSizeDp.pixels
verticalPosition = if (flip) Position.Vertical.Bottom else Position.Vertical.Top
}
LabelPosition.BelowPoint -> {
val bottomPointY =
targets.maxOf { target ->
when (target) {
is CandlestickCartesianLayerMarkerTarget -> target.lowCanvasY
is ColumnCartesianLayerMarkerTarget ->
target.columns.maxOf(ColumnCartesianLayerMarkerTarget.Column::canvasY)
is LineCartesianLayerMarkerTarget ->
target.points.maxOf(LineCartesianLayerMarkerTarget.Point::canvasY)
else -> error("Unexpected `CartesianMarker.Target` implementation.")
}
}
tickPosition = MarkerCorneredShape.TickPosition.Top
y = bottomPointY + tickSizeDp.pixels
verticalPosition = Position.Vertical.Bottom
}
}
markerCorneredShape?.tickPosition = tickPosition
label.draw(
context = context,
text = text,
x = targetX,
y = y,
verticalPosition = verticalPosition,
maxWidth = layerBounds.width().toInt(),
)
}
}
@Composable
public fun rememberCustomCartesianMarker(
label: TextComponent,
valueFormatter: DefaultCartesianMarker.ValueFormatter = remember {
DefaultCartesianMarker.ValueFormatter.default()
},
labelPosition: DefaultCartesianMarker.LabelPosition = DefaultCartesianMarker.LabelPosition.Top,
indicator: ((Color) -> Component)? = null,
indicatorSize: Dp = Defaults.MARKER_INDICATOR_SIZE.dp,
guideline: LineComponent? = null,
): DefaultCartesianMarker =
remember(label, valueFormatter, labelPosition, indicator, indicatorSize, guideline) {
CustomCartesianMarker(
label = label,
valueFormatter = valueFormatter,
labelPosition = labelPosition,
indicator = if (indicator != null) ({ indicator(Color(it)) }) else null,
indicatorSizeDp = indicatorSize.value,
guideline = guideline,
)
}
|
Beta Was this translation helpful? Give feedback.
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
-
Question
I have a chart where each value marker has an icon on top of the corresponding point. The "problem" is that as I am scrolling through the chart the icon doesn't stay fixed on top of the graph point (see the video).
To draw an icon on top of each marker I'm using the ValueFormatter with the following
which I then apply as normal to the marker
Is there anything I can do so the icons don't slide?
Screen.Recording.2025-03-26.at.09.33.23.mov
Module(s)
compose
Platform(s)
Android
Platform version(s)
No response
Vico version(s)
2.1.1
Beta Was this translation helpful? Give feedback.
All reactions