Jetpack Compose already offers us a huge library of components to create our UI. But in order to create a more delight and custom experience to the user, we sometimes need to create our own.
In my first article series I want to talk about how to create custom views in Jetpack Compose.
On the example of a Labeled Ranged Slider we go though the steps needed to create a fully custom Composable. I chose this example because the current version of the Range Slider in Compose only offers a Float Range and no easy way to display step aligned labels, changing its appears in accordance to the selected range.
The result of this series will look like this
We will look at three different topics used to create the Composable and in the end we put it all together.
- How to draw on a Canvas (This part)
- Handling touch interaction
- Kotlin Context Receivers (because it’s new and fun :-D)
- Draw the Labeled Range Slider
- Interact with the Labeled Range slider
All the code and projects will be published on GitHub as we go along.
How to draw on a Canvas
Let’s start with looking at how to draw something on a Canvas within a Composable.
As a first simple step we draw a basic shape, like a circle. To do this we
Create our Composable
- Give it a minimum size
- Add a Canvas
- Draw the circle
@Composable
fun DrawSimpleCircle(
color: Color,
modifier: Modifier = Modifier,
minSize: DpSize = DpSize(32.dp, 32.dp)
) {
Canvas(modifier = modifier.size(minSize)) {
drawCircle(color)
}
}
The result of this Composable will be a circle in the given color, filling the size of the whole Composable, e.g.
Additionally this API allows us to draw our shapes not only with a single color, but also offers a more flexible Brush variant. With a Brush we are able to achieve gradients as well. The example shows a gradient with two colors, but the API allows for a variable list of colors, if desired.
...
drawCircle(
brush = Brush.radialGradient(
colors = listOf(
centerColor, outerColor
)
)
)
A Canvas in Compose can of course not only draw circles. It offers a multitude of different drawing methods. Here are examples of the offers methods and a what it might look like.
drawRect
drawRoundRect
drawLine
drawOval
drawArc
...
drawArc(
color = color,
startAngle = 180f, // 0f is at 3 o'clock of the circle
sweepAngle = 180f, // go 180° from startAngle
useCenter = false, // whether to fill the arc or not
style = Stroke(width = 2.dp.toPx())
)
drawPath
...
val path = Path().apply {
lineTo(size.width, 0f) // line from start to end
quadraticBezierTo( // arc from the top to bottom
size.width / 2, size.height / 2,
size.width, size.height
)
lineTo(0f, size.height) // line from end to start
quadraticBezierTo( // arc from bottom to top
size.width / 2, size.height / 2,
0f, 0f
)
close() // link end to start
}
drawPath(
color = color,
path = path,
style = Stroke(width = 2.dp.toPx())
)
drawPoints
drawOutline
drawImage
...
drawImage(
image = image,
dstSize = IntSize(size.width.toInt(), size.width.toInt()),
colorFilter = ColorFilter.lighting(Color.Yellow, Color.Red)
)
Native Canvas
One useful draw function that is not offered directly within the Canvas Composable is the ability to draw a text. But we can still achieve this by using the drawIntoCanvas method. It gives us access to the native Canvas, which offers a drawText method.
Important to note: When using the native Canvas you have to resort to the android.graphics package, using its Paint or Color classes instead of the Compose variants.
Canvas(modifier = modifier.size(minSize)) {
val paint = android.graphics.Paint()
paint.textSize = textSize.toPx()
paint.color = android.graphics.Color.BLACK
drawIntoCanvas {
it.nativeCanvas.drawText(text, 0f, 0f, paint)
}
}
Conclusion
With the Canvas and the native Canvas we are able to draw everything we need for our Labeled Range Slider.
In the next part we will take a look at how we can make our custom Composable interactive, by reacting on touch events. Go directly to part 2.
The code for creating the images in this post can be found on GitHub.
Top comments (0)