Graphics in Jetpack Compose
Graphics in Android has always has been a tricky and abandoned area due its complexity and the setup required to perform even basic drawing. Google saw that and made it easy and simple with the introduction of Jetpack Compose and it's easier API using Canvas
.
It is like Photoshop but for developers, and literally does what its called -- it provides a blank canvas for developers to draw on. Let's say your Android app requires some sort of custom graphics, it could be as simple as putting a circle in a box, or fine-grained control of graphic elements. Compose's declarative UI approach makes it simpler in an efficient way.
Canvas
Canvas acts exactly how you would expect a composable to function because under the hood Canvas
is basically a Spacer
with a drawBehind
modifier. Canvas exposes a drawScope
, but what even is the draw scope?
Draw Scope
Draw Scope is a scoped drawing environment that maintains its own state; it also provides pre-built functions like:
drawRect
drawRoundRect
drawCircle
drawOval
drawLine
drawArc
drawPath
drawPoints
Canvas Graph
Canvas can be customized to your heart's will with the help of X & Y axis
. Draw scope provides its default coordinates [0,0]
on the top left of the canvas. To understand how the coordinate system in compose works, let's take this graph as example.
the origin point [0,0]
of coordinate system is on the top left. X
- increases as it moves to the right and Y
- increases as it moves downwards.
Draw Rect
Implementation of drawRect
is simple, it has many parameters that you can pass to customize you drawRect
object
Canvas(
modifier = Modifier
.height(200.dp)
.width(300.dp)
) {
drawRect(
color = color,
size = size,
alpha = Float,
style = DrawStyle,
blendMode = BlendMode,
colorFilter = ColorFilter,
topLeft = Offset
)
}
In the following code, we define a height
and width
to shape it like a rectangle. You can modify these parameters to customize your rectangle, check GitHub
repo below to look at the implementation here.
The advantage of using Canvas in Jetpack Compose is that many of its concepts are similar, so we will concentrate solely on the parameters that are specific to them.
Draw Circle
To learn how center offset
works in drawCircle
. We can give our canvas a background. Center
in drawCircle
is the X & Y
coordinate offset of the center point of our drawCircle
object. X moves to the left/right and Y and moves upward/downward.
Code sample:
Canvas(
modifier = Modifier
.size(20.dp)
.background(color = Color.Magenta)
) {
drawCircle(
color = color,
center = Offset(
x = size.height,
y = size.width
),
)
}
Thus center point moves to bottom right of the canvas.
drawCircle
and drawOval
both follow basic principles except drawOval
takes dimensions of the rectangle to draw.
Draw Line
drawLine
takes two offsets to draw the line on screen start [X&Y]
and end [X&Y]
then draws a straight line from the straight point to the endpoint,
we can also add stroke width to make the line more visible. PathEffect
opens up a whole bunch of customization capabilities, and lets you add multiple kinds of effects, like dashEffect
, make it a dotted line, and so on.
XO board drawn using drawLine
Draw Arc
Arc takes the dimensions of a circle canvas. It starts from startAngle
and sweeps to sweepAngle
. Sweep Angle draws relative to start angle
With positive sweep values it travels clockwise and anti-clockwise for negative values.
The useCenter
parameter indicates if the arc should be close to the center of the arc or relative to topLeft
.
Draw Path & Draw Point
drawPath
and drawPoint
are pretty similar to each other. In drawPath
to draw a path first we need to create a reference to Path()
method to create coordinates or give directions for the canvas to draw,
For example:
Output:
But how does it get its shape and look like a right-angled triangle? Well, to break it down let's change its draw style to stroke
.
Now we can understand better how it works -- we first move our coordinates to the width of the screen and then draw lines. But it still doesn't answer the right-angled shape, you say?
Since the default draw style is filled Path()
, the sub-paths within it are implicitly closed and thus we get a right-angled triangle.
for drawPoint
on the other hand you pass list of offsets
you want canvas to draw on.
Output:
draw points have multiple pointModes
to customize paths.
PointMode.Points
PointMode.Polygon
PointMode.Lines
Advantages Of Canvas
- Compose minimizes state in its graphics element avoiding state pitfalls.
- Everything you draw is a composable function.
- Compose behind the scene takes care of creating and freezing objects efficiently.
Bonus Links
Github Source Code:
Learn more about draw scope here:
Learn more about Compose:
Wanna try something cross platform? learn Flutter:
Spice things up with an introduction to AOSP: