Created
August 18, 2024 17:18
-
-
Save decodeandroid/da5cd2b90735e4d72b3e0f2d5b58104a to your computer and use it in GitHub Desktop.
Pattern View Jetpack Compose
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import android.view.MotionEvent | |
| import androidx.compose.foundation.Canvas | |
| import androidx.compose.foundation.background | |
| import androidx.compose.foundation.clickable | |
| import androidx.compose.foundation.layout.Arrangement | |
| import androidx.compose.foundation.layout.Column | |
| import androidx.compose.foundation.layout.fillMaxSize | |
| import androidx.compose.foundation.layout.fillMaxWidth | |
| import androidx.compose.foundation.layout.height | |
| import androidx.compose.material3.Text | |
| import androidx.compose.runtime.Composable | |
| import androidx.compose.runtime.getValue | |
| import androidx.compose.runtime.mutableIntStateOf | |
| import androidx.compose.runtime.mutableStateListOf | |
| import androidx.compose.runtime.mutableStateOf | |
| import androidx.compose.runtime.remember | |
| import androidx.compose.runtime.setValue | |
| import androidx.compose.ui.Alignment | |
| import androidx.compose.ui.ExperimentalComposeUiApi | |
| import androidx.compose.ui.Modifier | |
| import androidx.compose.ui.geometry.Offset | |
| import androidx.compose.ui.graphics.Color | |
| import androidx.compose.ui.graphics.Path | |
| import androidx.compose.ui.graphics.drawscope.Stroke | |
| import androidx.compose.ui.input.pointer.pointerInteropFilter | |
| import androidx.compose.ui.tooling.preview.Preview | |
| import androidx.compose.ui.unit.dp | |
| import androidx.compose.ui.unit.sp | |
| import kotlin.math.sqrt | |
| @Preview(showBackground = true, showSystemUi = true) | |
| @Composable | |
| fun PreviewPattern() { | |
| PatternView() | |
| } | |
| @OptIn(ExperimentalComposeUiApi::class) | |
| @Composable | |
| fun PatternView() { | |
| val rowCount = 3 | |
| val columnCount = 3 | |
| val selectedCellsList = remember { mutableStateListOf<CellModel>() } | |
| var selectedCellIndex by remember { mutableIntStateOf(0) } | |
| val selectedCellsIndexList = remember { | |
| mutableStateListOf<Int?>() | |
| } | |
| val selectedCellCenterList = remember { mutableStateListOf(Offset.Zero) } | |
| var text by remember { | |
| mutableStateOf("Draw Your Pattern") | |
| } | |
| Column( | |
| modifier = Modifier.fillMaxSize(), | |
| verticalArrangement = Arrangement.SpaceEvenly, | |
| horizontalAlignment = Alignment.CenterHorizontally | |
| ) { | |
| Text(text = text, color = Color.Black, fontSize = 20.sp) | |
| Canvas( | |
| modifier = Modifier | |
| .fillMaxWidth() | |
| .height(400.dp) | |
| .background(Color.White) | |
| .pointerInteropFilter { | |
| when (it.action) { | |
| MotionEvent.ACTION_DOWN -> { | |
| val cell = CellModel( | |
| offset = Offset(it.x, it.y) | |
| ) | |
| //add new cell to list | |
| selectedCellsList.add(cell) | |
| //clear previous cells centers | |
| selectedCellCenterList.clear() | |
| } | |
| MotionEvent.ACTION_MOVE -> { | |
| text = "Release Finger When Done" | |
| val cell = CellModel( | |
| offset = Offset(it.x, it.y) | |
| ) | |
| selectedCellsList.add(cell) | |
| } | |
| MotionEvent.ACTION_UP -> { | |
| text = "Selected Index : ${selectedCellsIndexList.toList()}" | |
| } | |
| } | |
| true | |
| } | |
| ) { | |
| val width = size.width | |
| val height = size.height | |
| val boxSizeInX = width / 3 | |
| val boxCenterInX = boxSizeInX / 2 | |
| val boxSizeInY = height / 3 | |
| val boxCenterInY = boxSizeInY / 2 | |
| val circleRadius = width / 8 | |
| //to draw all dots | |
| for (row in 0..<rowCount) { | |
| for (column in 0..<columnCount) { | |
| drawCircle( | |
| color = Color.Black, | |
| radius = 25f, | |
| center = Offset((boxCenterInX + boxSizeInX * column), (boxCenterInY + boxSizeInY * row)) | |
| ) | |
| } | |
| } | |
| selectedCellsList.forEachIndexed { index, offset -> | |
| //first find the index of each cell we have selected | |
| //index will vary b/w 0,1,2 | |
| val columnIndex = (offset.offset.x / width * columnCount).toInt() | |
| val rowIndex = (offset.offset.y / height * rowCount).toInt() | |
| val pathOffset = Offset(offset.offset.x, offset.offset.y) | |
| val currentCellCenter = | |
| Offset((boxCenterInX + boxSizeInX * columnIndex), (boxCenterInY + boxSizeInY * rowIndex)) | |
| //use pythagoras theorem -> r*r = X*X + Y*Y | |
| val distanceFromCenter = sqrt( | |
| (pathOffset.x - currentCellCenter.x) * (pathOffset.x - currentCellCenter.x) + | |
| ((pathOffset.y - currentCellCenter.y) * (pathOffset.y - currentCellCenter.y)) | |
| ) | |
| //we will only include a cell if we move fingers only through a specific radius around the cell | |
| if (distanceFromCenter < circleRadius) { | |
| selectedCellIndex = columnIndex + 1 + rowIndex * columnCount | |
| if (!selectedCellsIndexList.contains(selectedCellIndex) && selectedCellIndex > 0) { | |
| selectedCellsIndexList.add(selectedCellIndex) | |
| } | |
| if (!selectedCellCenterList.contains(currentCellCenter)) { | |
| selectedCellCenterList.add(currentCellCenter) | |
| } | |
| drawCircle( | |
| offset.color, | |
| center = currentCellCenter, | |
| radius = 50f, | |
| style = Stroke(offset.strokeWidth) | |
| ) | |
| } | |
| } | |
| //to draw green line behind | |
| if (selectedCellCenterList.size > 1) { | |
| //First create path for line | |
| val path = Path().apply { | |
| //start from first circle and draw line till the end one | |
| selectedCellCenterList.forEachIndexed { index, offset -> | |
| if (index == 0) { | |
| moveTo(offset.x, offset.y) | |
| } else { | |
| lineTo( | |
| (offset.x), | |
| (offset.y) | |
| ) | |
| } | |
| } | |
| } | |
| //finally draw the path | |
| drawPath(path, Color.Blue.copy(0.5f), style = Stroke(15f)) | |
| } | |
| } | |
| Text(modifier = Modifier.clickable { | |
| selectedCellsList.clear() | |
| selectedCellsIndexList.clear() | |
| selectedCellCenterList.clear() | |
| text = "Draw Your Pattern" | |
| }, text = "Clear", color = Color.Blue.copy(0.5f), fontSize = 20.sp) | |
| } | |
| } | |
| data class CellModel( | |
| val offset: Offset, | |
| val color: Color = Color.Blue.copy(0.5f), | |
| val strokeWidth: Float = 5f | |
| ) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment