Home > Software engineering >  Recompose a Canvas while maintaining the previous drawing
Recompose a Canvas while maintaining the previous drawing

Time:11-24

This Function draw figures on canvas but when I clicked on button its shows only latest clicked figure but I want to show all figures which was clicked previous. I think drawScope state can be remembered as a mutable state but how to achieve it.

Please help...

    @Composable
    fun PaintBrushLayout(
        modifier: Modifier = Modifier,
    ) {
var clicked by remember {
    mutableStateOf(false)
}
var figName by remember {
    mutableStateOf("")
}

Column(
    verticalArrangement = Arrangement.Bottom, modifier = modifier.fillMaxSize()
) {
    
    Canvas(
        modifier = Modifier
            .fillMaxWidth()
            .fillMaxHeight(.9f)
    ) {
        Log.d("in Paint", "PaintBrushLayout: $this")
        if (clicked) {
                Log.d("TAG", "PaintBrushLayout: $figName")
                when (figName) {
                    "Circle" -> {
                        drawCircleFig(color = Color.Blue)
                    }
                    "Rectangle" -> {
                        drawRectangleFig()
                    }
                    "Oval" -> {
                        drawOvalFig(color = Color.Gray)
                    }

                }
        }

    }

    Row(
        modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
    ) {

        BottomLayout("Circle") { click, name ->
            clicked = click
            figName = name
        }
        BottomLayout("Rectangle") { click, name ->
            clicked = click
            figName = name
        }
        BottomLayout("Oval") { click, name ->
            clicked = click
            figName = name
        }
        BottomLayout("Choose Color") { click, name ->
            clicked = click
           // figName = name
        }
    }
}
    }

Create Bottom Layout of Buttons

    @Composable
    fun BottomLayout(
btnName: String,
click: (Boolean, String) -> Unit
    ) {
Button(onClick = {
    click(true, btnName)

}, modifier = Modifier.padding(4.dp)) {
    Text(text = btnName)
}

    }

Draw Circle Figure on canvas

    fun DrawScope.drawCircleFig(color: Color = Color.Red) {
drawCircle(
    color = color
)
    }

Draw Rectangle Figure on canvas

    fun DrawScope.drawRectangleFig(color: Color = Color.Red) {
drawRect(
    color = color
)
    }

Draw Oval Figure on canvas

    fun DrawScope.drawOvalFig(color: Color = Color.Red) {
drawOval(
    color = color
)
    }

CodePudding user response:

I don't know if it's possible to recompose a Canvas and maintain its state prior to what it was previously, allowing you to drawn on top of what is already there. One solution is to keep track of a queue of the operations that you want to perform and then each time the Canvas is recomposed, you iterate through the queue and draw each item. If this is a drawingn app, it has the benefit of allowing the user to undo the previous steps. I also noticed that the code you provided doesn't recompose when you click on a button. I've noticed this issue before. The solution is to assign the remembered value to another value. Here is the sample on using a queue:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            val queue = mutableListOf<DrawingObject>()
            PaintBrushLayout(queue = queue)
        }
    }
}

@Composable
fun PaintBrushLayout(
    modifier: Modifier = Modifier,
    queue: MutableList<DrawingObject>
) {
    var currentDrawingObject by remember { mutableStateOf(DrawingObject.None) }
    var c = currentDrawingObject // This fixes a bug in recomposition.

    fun addToQueue(drawingObject: DrawingObject) {
        queue.add(drawingObject)
        currentDrawingObject = drawingObject
    }

    Column(
        verticalArrangement = Arrangement.Bottom, modifier = modifier.fillMaxSize()
    ) {

        Canvas(
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight(.9f)
        ) {
            queue.forEach { drawingObject ->
                when (drawingObject) {
                    DrawingObject.Circle -> {
                        drawCircleFig(color = Color.Blue)
                    }
                    DrawingObject.Rectangle -> {
                        drawRectangleFig()
                    }
                    DrawingObject.Oval -> {
                        drawOvalFig(color = Color.Gray)
                    }
                }
            }
        }

        Row(
            modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween
        ) {

            BottomLayout("Circle") { click, name ->
                addToQueue(DrawingObject.Circle)
            }
            BottomLayout("Rectangle") { click, name ->
                addToQueue(DrawingObject.Rectangle)
            }
            BottomLayout("Oval") { click, name ->
                addToQueue(DrawingObject.Oval)
            }
            BottomLayout("Choose Color") { click, name ->

            }
        }
    }
}

@Composable
fun BottomLayout(
    btnName: String,
    click: (Boolean, String) -> Unit
) {
    Button(onClick = {
        click(true, btnName)

    }, modifier = Modifier.padding(4.dp)) {
        Text(text = btnName)
    }
}

fun DrawScope.drawCircleFig(color: Color = Color.Red) {
    drawCircle(
        radius = 300f,
        color = color
    )
}

fun DrawScope.drawRectangleFig(color: Color = Color.Red) {
    drawRect(
        size = Size(200f, 200f),
        color = color
    )
}

fun DrawScope.drawOvalFig(color: Color = Color.Red) {
    drawOval(
        size = Size(100f, 100f),
        color = color
    )
}

enum class DrawingObject {
    None,
    Circle,
    Rectangle,
    Oval
}

Canvas with multiple drawing images

  • Related