'how to progressively add drawable to a canvas?

I have points generated one by one, and when a new point is generated, I want to draw a line segment connecting with the previous point. Like this:

var x by remember { mutableStateOf( 0.0f)}
var y by remember { mutableStateOf( 0.5f)}
var pStart by remember { mutableStateOf(Offset(0f, 0.5f))}

Canvas(modifier = Modifier.fillMaxSize()) {
    canvasWidth = size.width
    canvasHeight = size.height
    val pEnd = Offset(x * canvasWidth, (1-y) * canvasHeight)
    val col = if (pEnd.y < pStart.y) Color.Green else Color.Red
    drawLine(
        start = pStart,
        end = pEnd,
        strokeWidth = 4f,
        color = col
    )
    pStart = pEnd
}

But this only draws the segment in a flash and no segments stay on the screen.

I know I can save the points to a list and redraw all the segments whenever a new point is added. But I just hope to economize. Is it possible?



Solution 1:[1]

There's no practical other way. You COULD in fact, keep track of just two points, adding a whole new Canvas (all Transparent and Filling the maximum Size, stacked on top of one another), for each extra point that is added. This does seem a bit impractical, but maybe try it out and do some benchmarking to see which one checks out. This is the only OTHER way I could think of, where you do not have to store all the points and recompose every time a point is added, since all the other lines would technically be frozen in space.

In response to the somewhat (unreasonably) aggressive comment below, here's some sample code. I assume you have a stream of new points coming in so a LiveData object is assumed to be the source of that, which I shall be converting to a MutableState<T> for my use-case.

var latestPoint by liveData.collectAsState()
var recordedPoint by remember { mutableStateOf(latestPoint) }

var triggerDraw by remember { mutableStateOf(false) }

var canvasList = mutableStateListOf<@Composable () -> Unit>Canvas>() // Canvas is the Composable

if(triggerDraw){
  canvasList.add(
    Canvas(){ 
      /* you have the recordedPoint, and  latestPoint, simply draw a line here */
    }
  )
  triggerDraw = false
}

LaunchedEffect(latestPoint){
 triggerDraw = true
}

canvasList.forEach {
  it() // Invoke the Composable here
}

Thanks Dad!

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1