1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
| import android.graphics.Bitmap import androidx.compose.foundation.Canvas import androidx.compose.foundation.gestures.detectDragGestures import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.Composable import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Path import androidx.compose.ui.graphics.asAndroidPath import androidx.compose.ui.graphics.copy import androidx.compose.ui.graphics.drawscope.Stroke import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.onSizeChanged
data class DrawPath(val path: Path, val color: Color, val strokeWidth: Float) class DrawingController() { val paths = mutableStateListOf<DrawPath>()
val undoList = mutableStateListOf<DrawPath>()
var boardSize = mutableStateOf(Pair(0, 0))
val currentColor = Color.Black val currentStrokeWidth = 2f
fun undo() { if (paths.isNotEmpty()) { undoList.add(paths.get(paths.size - 1)) paths.remove(paths.get(paths.size - 1)) } }
fun redo() { if (undoList.size > 0) { val last = undoList.get(undoList.size - 1) paths.add(last) undoList.remove(last) } }
fun clear() { paths.clear() }
fun addPath(path: DrawPath) { paths.add(path) undoList.clear() }
fun getPaths(): List<DrawPath> = paths.toList()
fun createBitmap(): Bitmap? { val width = boardSize.value.first val height = boardSize.value.second
if (width <= 0 || height <= 0) return null
return Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888).apply { val canvas = android.graphics.Canvas(this) canvas.drawColor(android.graphics.Color.WHITE) getPaths().forEach { pathItem -> val paint = android.graphics.Paint().apply { color = pathItem.color.toArgb() strokeWidth = pathItem.strokeWidth style = android.graphics.Paint.Style.STROKE isAntiAlias = true } canvas.drawPath(pathItem.path.asAndroidPath(), paint) } } } }
@Composable fun DrawingBoard(controller: DrawingController, modifier: Modifier) { var currentPath by remember { mutableStateOf(Path()) } val paths by remember { derivedStateOf { controller.getPaths() } } Box(modifier = modifier) { Canvas( modifier = Modifier .fillMaxSize() .onSizeChanged { size -> controller.boardSize.value = Pair(size.width, size.height) } .pointerInput(Unit) { detectDragGestures( onDragStart = { offset -> currentPath = Path().apply { moveTo(offset.x, offset.y) } }, onDrag = { change, _ -> currentPath.lineTo(change.position.x, change.position.y) currentPath = currentPath.copy() }, onDragEnd = { controller.addPath( DrawPath( currentPath, controller.currentColor, controller.currentStrokeWidth ) ) currentPath = Path() } ) }, ) { paths.forEach { drawPath -> drawPath( path = drawPath.path, color = drawPath.color, style = Stroke(width = drawPath.strokeWidth) ) } drawPath( path = currentPath, color = controller.currentColor, style = Stroke(width = controller.currentStrokeWidth) ) } } }
|