Guilherme Almeida
08/26/2022, 4:10 PM@Composable
public fun NaiveWaveform(
waveform: Waveform,
progress: Float,
) {
Box(
modifier = Modifier
.width(256.dp)
.height(64.dp)
) {
Canvas(modifier = Modifier.fillMaxSize()) {
drawWaveform(waveform) // heavy drawing, rarely changes
drawProgress(progress) // changes every 500ms
}
}
}
The waveform is basically a wrapper for a FloatArray and progress is a float that goes from 0..1.
The problem here is that the waveform can have thousands of points to be drawn which makes it hard to be drawn alongside the progress line that updates every 500 ms.
Therefore I thought of creating two different Composables, one for each component. By doing that I should be able to leverage smart recomposition (Waveform is @Immutable) and only draw the waveform when it changes, keeping it independent from the progress line. Something like:
@Composable
public fun StackedWaveform(
waveform: Waveform,
progress: Float,
) {
Box(
Modifier
.width(256.dp)
.height(64.dp)
) {
WaveformCanvas(waveform = waveform)
ProgressCanvas(progress = progress)
}
}
@Composable
public fun ProgressCanvas(
progress: Float,
) {
println("ProgressCanvas")
Canvas(
modifier = Modifier.fillMaxSize(),
) {
println("Drawing Progress")
drawProgress(progress)
}
}
@Composable
public fun WaveformCanvas(
waveform: Waveform,
) {
println("WaveformCanvas")
Canvas(
modifier = Modifier.fillMaxSize(),
onDraw = {
println("Drawing waveform")
drawWaveform(waveform)
},
)
}
And now comes the bit I canât understand quite well, the logs for these (with the progress animating):
I/System.out: WaveformCanvas
I/System.out: ProgressCanvas
I/System.out: Drawing waveform
I/System.out: Drawing Progress
-- this initial setup makes sense đ, compose goes through each composable and draws on each Canvas, but then:
I/System.out: ProgressCanvas
I/System.out: Drawing waveform
I/System.out: Drawing Progress
I/System.out: ProgressCanvas
I/System.out: Drawing waveform
I/System.out: Drawing Progress
...
(repeats while progress is increasing)
Ideally, that second section in the logs should only have âProgressCanvasâ and âDrawing Progressâ logs, but somehow it is also redrawing the waveform!
Looking at the logs, Compose seems to be skipping WaveformCanvas
(it only calls it once in the beggining, as intended), but moving forward the canvas inside still redraws the waveform everytime the progress changes đ€
If anyone knows a better way to approach this please let know, thanks in advance!Leland Richardson [G]
08/26/2022, 4:16 PMLeland Richardson [G]
08/26/2022, 4:18 PMBox(
Modifier
.drawBehind { ... draw waveform ... }
.graphicsLayer()
.drawBehind { ... draw progress ... }
)
Various ways to do this, but by having a graphicsLayer()
in between, when the progress drawing gets invalidated, it will restart drawing after the waveformGuilherme Almeida
08/26/2022, 4:26 PM@Composable
public fun BoxWaveform(
waveform: Waveform,
progress: Float,
) {
Box(
Modifier
.width(256.dp)
.height(128.dp)
.drawBehind {
println("Drawing waveform")
drawWaveform(waveform)
}
.graphicsLayer()
.drawBehind {
println("Drawing progress")
drawProgress(progress)
}
)
}
Leland Richardson [G]
08/26/2022, 4:27 PMLeland Richardson [G]
08/26/2022, 4:28 PMLeland Richardson [G]
08/26/2022, 4:28 PMGuilherme Almeida
08/26/2022, 4:31 PMLeland Richardson [G]
08/26/2022, 4:32 PMwaveform == prevWaveform
in general if it hasnât changed here?Michael Paus
08/26/2022, 4:33 PMModifier.drawWithCache
might also be a useful approach.Leland Richardson [G]
08/26/2022, 4:33 PMGuilherme Almeida
08/26/2022, 4:34 PMLeland Richardson [G]
08/26/2022, 4:35 PMval drawWaveform = remember(waveform) { Modifier.drawBehind { ... waveform ... } }
Box(
Modifier
.then(drawWaveform)
.graphicsLayer()
.drawBehind { ... progress ... }
)
Guilherme Almeida
08/26/2022, 4:39 PMLeland Richardson [G]
08/26/2022, 4:40 PMGuilherme Almeida
08/26/2022, 4:47 PMremember
didnât work for me, but it makes sense đ
Update on the other idea, I only need the graphicsLayer on the ProgressCanvas
, the WaveformCanvas
works fine without itGuilherme Almeida
08/26/2022, 4:47 PM@Composable
public fun ProgressCanvas(
progress: Float,
) {
println("ProgressCanvas")
Box(
Modifier
.fillMaxSize()
.graphicsLayer()
.drawBehind {
println("Drawing Progress")
drawProgress(progress)
}
)
}
@Composable
public fun WaveformCanvas(
waveform: Waveform,
) {
println("WaveformCanvas")
Box(
Modifier
.fillMaxSize()
.drawBehind {
println("Drawing waveform")
drawWaveform(waveform)
}
)
}