Skip to content

Commit 65046d3

Browse files
committed
Adds DrawArea sample
This commit introduces a new code snippet demonstrating a `DrawArea` composable. This composable provides a canvas for drawing and processes touch input to draw lines. The drawing state is managed by a `DrawAreaViewModel`. The sample is also integrated into the main snippets navigation.
1 parent 97d93a1 commit 65046d3

File tree

3 files changed

+139
-1
lines changed

3 files changed

+139
-1
lines changed
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
package com.example.android.snippets
2+
3+
import android.os.Bundle
4+
import android.util.Log
5+
import androidx.activity.ComponentActivity
6+
import androidx.activity.compose.setContent
7+
import androidx.compose.foundation.Canvas
8+
import androidx.compose.foundation.background
9+
import androidx.compose.foundation.layout.Column
10+
import androidx.compose.foundation.layout.fillMaxSize
11+
import androidx.compose.runtime.Composable
12+
import androidx.compose.runtime.mutableStateListOf
13+
import androidx.compose.runtime.snapshots.SnapshotStateList
14+
import androidx.compose.ui.ExperimentalComposeUiApi
15+
import androidx.compose.ui.Modifier
16+
import androidx.compose.ui.draw.clipToBounds
17+
import androidx.compose.ui.graphics.Color
18+
import androidx.compose.ui.graphics.Path
19+
import androidx.compose.ui.graphics.StrokeCap
20+
import androidx.compose.ui.graphics.drawscope.Stroke
21+
import androidx.compose.ui.input.pointer.pointerInteropFilter
22+
import androidx.compose.ui.tooling.preview.Preview
23+
import androidx.lifecycle.ViewModel
24+
import androidx.lifecycle.viewmodel.compose.viewModel
25+
26+
/*
27+
* Copyright 2023 The Android Open Source Project
28+
*
29+
* Licensed under the Apache License, Version 2.0 (the "License");
30+
* you may not use this file except in compliance with the License.
31+
* You may obtain a copy of the License at
32+
*
33+
* https://www.apache.org/licenses/LICENSE-2.0
34+
*
35+
* Unless required by applicable law or agreed to in writing, software
36+
* distributed under the License is distributed on an "AS IS" BASIS,
37+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
38+
* See the License for the specific language governing permissions and
39+
* limitations under the License.
40+
*/
41+
42+
/**
43+
* This sample demonstrates a Composable function that provides a canvas for drawing and
44+
* processes touch input events to draw lines. It uses a ViewModel to manage the drawing state.
45+
*
46+
* Gradle Dependencies:
47+
* implementation "androidx.compose.ui:ui:1.x.x"
48+
* implementation "androidx.compose.foundation:foundation:1.x.x"
49+
* implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.x.x"
50+
*
51+
* Manifest Permissions:
52+
* None
53+
*/
54+
55+
const val TAG = "DrawAreaSample"
56+
57+
class DrawAreaSampleActivity : ComponentActivity() {
58+
override fun onCreate(savedInstanceState: Bundle?) {
59+
super.onCreate(savedInstanceState)
60+
setContent {
61+
Column(Modifier.fillMaxSize().background(Color.LightGray)) {
62+
DrawArea(modifier = Modifier.weight(1f))
63+
}
64+
}
65+
}
66+
}
67+
68+
// [START draw_area_sample]
69+
@Composable
70+
@OptIn(ExperimentalComposeUiApi::class)
71+
fun DrawArea(modifier: Modifier = Modifier, viewModel: DrawAreaViewModel = viewModel()) {
72+
Canvas(
73+
modifier = modifier
74+
.clipToBounds()
75+
.pointerInteropFilter { motionEvent ->
76+
viewModel.processMotionEvent(motionEvent)
77+
true
78+
}
79+
) {
80+
viewModel.paths.forEach { path ->
81+
drawPath(
82+
path = path,
83+
color = Color.Black,
84+
style = Stroke(width = 8f, cap = StrokeCap.Round)
85+
)
86+
}
87+
}
88+
}
89+
// [END draw_area_sample]
90+
91+
class DrawAreaViewModel : ViewModel() {
92+
private var currentPath = Path()
93+
val paths: SnapshotStateList<Path> = mutableStateListOf()
94+
95+
@OptIn(ExperimentalComposeUiApi::class)
96+
fun processMotionEvent(motionEvent: android.view.MotionEvent) {
97+
when (motionEvent.actionMasked) {
98+
android.view.MotionEvent.ACTION_DOWN -> {
99+
Log.d(TAG, "ACTION_DOWN at (${motionEvent.x}, ${motionEvent.y})")
100+
currentPath = Path().apply {
101+
moveTo(motionEvent.x, motionEvent.y)
102+
}
103+
paths.add(currentPath)
104+
}
105+
android.view.MotionEvent.ACTION_MOVE -> {
106+
Log.d(TAG, "ACTION_MOVE at (${motionEvent.x}, ${motionEvent.y})")
107+
// To trigger recomposition, we need to replace the path object
108+
// in the list with a new one.
109+
val newPath = Path().apply {
110+
addPath(currentPath)
111+
lineTo(motionEvent.x, motionEvent.y)
112+
}
113+
114+
// Replace the last path with the updated one.
115+
if (paths.isNotEmpty()) {
116+
paths[paths.size - 1] = newPath
117+
}
118+
currentPath = newPath
119+
}
120+
android.view.MotionEvent.ACTION_UP,
121+
android.view.MotionEvent.ACTION_CANCEL -> {
122+
Log.d(TAG, "ACTION_UP/CANCEL")
123+
// Path is complete, no further action needed
124+
}
125+
}
126+
}
127+
}
128+
129+
@Preview(showBackground = true)
130+
@Composable
131+
fun PreviewDrawArea() {
132+
Column(Modifier.fillMaxSize().background(Color.LightGray)) {
133+
DrawArea(modifier = Modifier.weight(1f))
134+
}
135+
}

compose/snippets/src/main/java/com/example/compose/snippets/SnippetsActivity.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ import com.example.compose.snippets.layouts.PagerExamples
6262
import com.example.compose.snippets.navigation.Destination
6363
import com.example.compose.snippets.navigation.TopComponentsDestination
6464
import com.example.compose.snippets.ui.theme.SnippetsTheme
65+
import com.example.android.snippets.DrawArea
6566

6667
class SnippetsActivity : ComponentActivity() {
6768
override fun onCreate(savedInstanceState: Bundle?) {
@@ -94,6 +95,7 @@ class SnippetsActivity : ComponentActivity() {
9495
Destination.ShapesExamples -> ApplyPolygonAsClipImage()
9596
Destination.SharedElementExamples -> PlaceholderSizeAnimated_Demo()
9697
Destination.PagerExamples -> PagerExamples()
98+
Destination.DrawAreaSample -> DrawArea()
9799
}
98100
}
99101
}

compose/snippets/src/main/java/com/example/compose/snippets/navigation/Destination.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ enum class Destination(val route: String, val title: String) {
2424
ScreenshotExample("screenshotExample", "Screenshot Examples"),
2525
ShapesExamples("shapesExamples", "Shapes Examples"),
2626
SharedElementExamples("sharedElement", "Shared elements"),
27-
PagerExamples("pagerExamples", "Pager examples")
27+
PagerExamples("pagerExamples", "Pager examples"),
28+
DrawAreaSample("drawAreaSample", "Draw Area Sample")
2829
}
2930

3031
// Enum class for compose components navigation screen.

0 commit comments

Comments
 (0)