-
-
Notifications
You must be signed in to change notification settings - Fork 368
/
CustomViewPaintActivity.kt
98 lines (92 loc) · 4.27 KB
/
CustomViewPaintActivity.kt
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
package com.example.jetpackcompose.customview
import android.os.Bundle
import android.view.MotionEvent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateListOf
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.StrokeJoin
import androidx.compose.ui.graphics.drawscope.Stroke
import androidx.activity.compose.setContent
import androidx.compose.runtime.remember
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.input.pointer.pointerInteropFilter
/**
* This example needs some more work.
*/
class CustomViewPaintActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// This is an extension function of Activity that sets the @Composable function that's
// passed to it as the root view of the activity. This is meant to replace the .xml file
// that we would typically set using the setContent(R.id.xml_file) method. The setContent
// block defines the activity's layout.
setContent {
CustomDrawableViewComponent()
}
}
}
// We represent a Composable function by annotating it with the @Composable annotation. Composable
// functions can only be called from within the scope of other composable functions. We should
// think of composable functions to be similar to lego blocks - each composable function is in turn
// built up of smaller composable functions.
@Composable
fun CustomDrawableViewComponent() {
DrawingBoardComposable()
}
data class Paths(
val x: Float,
val y: Float
)
@OptIn(ExperimentalComposeUiApi::class)
@Composable
fun DrawingBoardComposable() {
// Reacting to state changes is the core behavior of Compose. You will notice a couple new
// keywords that are compose related - remember & mutableStateOf.remember{} is a helper
// composable that calculates the value passed to it only during the first composition. It then
// returns the same value for every subsequent composition. Next, you can think of
// mutableStateOf as an observable value where updates to this variable will redraw all
// the composable functions that access it. We don't need to explicitly subscribe at all. Any
// composable that reads its value will be recomposed any time the value
// changes. This ensures that only the composables that depend on this will be redraw while the
// rest remain unchanged. This ensures efficiency and is a performance optimization. It
// is inspired from existing frameworks like React.
val paths = remember { mutableStateListOf<Paths>() }
// Column is a composable that places its children in a vertical sequence. You
// can think of it similar to a LinearLayout with the vertical orientation. In addition, we
// also provide the column with a modifier.
// You can think of Modifiers as implementations of the decorators pattern that are used to
// modify the composable that its applied to. In this example, as the Box composable to
// occupy the entire available height & width using Modifier.fillMaxSize().
Column(
modifier = Modifier
.fillMaxSize()
// pointerInteropFilter gives you access to MotionEvent's that are received by this
// composable.
.pointerInteropFilter {
when (it.actionMasked) {
MotionEvent.ACTION_DOWN -> {
paths += Paths(it.x, it.y)
true
}
else -> false
}
}
) {
// We use the Canvas composable that gives you access to a canvas that you can draw
// into. We also pass it a modifier.
Canvas(modifier = Modifier) {
val p = Path()
for (path in paths) {
p.lineTo(path.x, path.y)
p.moveTo(path.x, path.y)
}
drawPath(p, color = Color.Black, style = Stroke(width = 3f, join = StrokeJoin.Round))
}
}
}