Reputation: 3000
I'm using Jetpack Compose to implement my requirement of drawing a 'Gradual growing line', i.e a line that starts from a specified start and point and gradually 'grows' until it reaches a specified end point. I had earlier implemented this with Custom view which worked fine. My logic requires the 'onDraw()' to be re-called again and again based on some condition. For this, I had used 'invalidate()' while using Custom view. But now I'm using Jetpack Compose and unable to find a way to recompose 'Canvas'.
Here is my Jetpack compose code for 'gradual growing line':
@Composable
fun SplashUI() {
var test = remember { mutableStateOf(0) }
Canvas(modifier = Modifier.fillMaxSize()) {
// starting point
x1 = 0.0;
y1 = size.height / 2.0;
// ending point
x2 = size.width.toDouble()
y2 = size.height / 2.0;
divideLineIntoEqualParts();
if (test.value < listOfPoints.size) {
drawLine(
Color.Black,
Offset(
listOfPoints.get(0)?.x!!,
listOfPoints.get(0)?.y!!
),
Offset(
listOfPoints.get(inte)?.x!!,
listOfPoints.get(inte)?.y!!
),
strokeWidth = 5f
);
}
test.value = test.value + 1 //This doesn't trigger recomposition of canvas
//Recomposition of Canvas should happen after the above step for my logic to work
//I had implemented this earlier using custom view, and used 'invalidate()' like:
/* if (inte < listOfPoints.size) {
invalidate()
}*/
}
}
private fun divideLineIntoEqualParts() {
listOfPoints.clear()
for (k in 1..50) {
listOfPoints.add(PointF((x1 + k * (x2 - x1) / 50).toFloat(),
(y1 + k * (y2 - y1) / 50).toFloat()
))
}
Log.d("listOfPoints : size : ", listOfPoints.size.toString() + "")
}
Please suggest a way I can recompose Canvas, or some other way to make my logic work.
Upvotes: 5
Views: 8434
Reputation: 2656
To emulate a timed game loop you can use the withFrame
function family
LaunchedEffect(Unit) {
while (true) {
withFrameNanos {
game. Update(it)
}
}
}
Upvotes: 2
Reputation: 8252
I agree with Gabriele Mariotti's answer but I've found sometimes you need the control so to be able to redraw any time you want in canvas !
I was trying to develop an app for drawing and when the touch event happens I have to invalidate !
In this article Simple Jetpack Drawing App The canvas was invalidated by the change of MotionEvent's action so everytime the action of the motion event changes the canvas can be invalidated (redrawn)
We can use the same logic
var invalidations by remember{
mutableStateOf(0)
}
Now in Canvas Composable
Canvas {
invalidations.let{inv->
//Draw Composables
}
}
Now whenever you want to invalidate :
invalidations++
This is not the best way to do this since changing state of one variable causes only that composable to redrawn but invalidating this way will redraw anything that is drawn inside invalidations.let block !
Upvotes: 10
Reputation: 364211
There are some differences.
The code in the question works in an Android View
, and you are drawing on the Canvas
in the onDraw
method. You are drawing an horizontal line dividing the available space (=width) in points.
In Compose you are just using a Canvas
as a Composable
, and you can animate the length of the line.
Something like:
//Animation - it will be repeated 2 times
val animatedProgress = remember { Animatable(0.001f) }
LaunchedEffect(animatedProgress) {
animatedProgress.animateTo(1f,
animationSpec = repeatable(2,
animation = tween(durationMillis = 3000, easing = LinearEasing)
))
}
Canvas(modifier = Modifier.fillMaxSize() ) {
val startingPoint = Offset(0f, size.height / 2f)
val endingPoint = Offset(size.width * animatedProgress.value, size.height / 2f)
//At the end of animation just remove the line
if (animatedProgress.isRunning) {
drawLine(
strokeWidth = 8.dp.toPx(),
color = Color.Black,
start = startingPoint,
end = endingPoint
)
}
}
Upvotes: 2