JetPack Compose — How To Draw Rectangle With Circular Cut Out
Jetpack Compose, the modern UI toolkit for Android, allows developers to create complex custom shapes with ease. In this article, we’ll walk through creating a custom shape: a rectangle with a circular cutout. We’ll define this shape in Kotlin using Jetpack Compose’s Shape
interface.
Step-by-Step Guide
To create a rectangle with a circular cutout, we’ll define a custom shape by implementing the Shape
interface. This involves overriding the createOutline
function to define the shape's path.
1. Define the Custom Shape Class
First, create a class called RectangleWithCircularCutOut
that implements the Shape
interface. This class will take two parameters: cornerRadius
and cutCornerRadius
, which define the sizes of the corners and the cutout,
class RectangleWithCircularCutOut(
private val cornerRadiusInDp: Dp = 0.dp,
private val cutCornerRadiusInDp: Dp,
) : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
val cornerRadius = with(density) { cornerRadiusInDp.toPx() }
val cutCornerRadius = with(density) { cutCornerRadiusInDp.toPx() }
return Outline.Generic(Path().apply {
reset()
// 1. Move to the starting point
moveTo(cornerRadius, 0f)
// 2. Draw the top-end corner arc
arcTo(
rect = Rect(
offset = Offset(size.width - cornerRadius, 0f),
size = Size(cornerRadius, cornerRadius)
),
startAngleDegrees = 270f,
sweepAngleDegrees = 90f,
forceMoveTo = false
)
// 3. Draw the circular cutout arc
arcTo(
rect = Rect(
offset = Offset(size.width - cutCornerRadius, size.height - cutCornerRadius),
size = Size(cutCornerRadius*2, cutCornerRadius*2)
),
startAngleDegrees = 0f,
sweepAngleDegrees = -270f,
forceMoveTo = false
)
// 4. Draw the bottom-start corner arc
arcTo(
rect = Rect(
offset = Offset(0f, size.height - cornerRadius),
size = Size(cornerRadius, cornerRadius)
),
startAngleDegrees = 90f,
sweepAngleDegrees = 90f,
forceMoveTo = false
)
// 5. Draw the top-start corner arc
arcTo(
rect = Rect(
offset = Offset(0f, 0f),
size = Size(cornerRadius, cornerRadius)
),
startAngleDegrees = 180f,
sweepAngleDegrees = 90f,
forceMoveTo = false
)
// 9. Close the path to complete the shape
close()
})
}
}
2. Using the Custom Shape in Jetpack Compose
Now that we have defined our custom shape, we can use it in our Jetpack Compose UI. Here’s an example of how to apply this shape to a Box
composable.
@Composable
fun CustomShapeDemo() {
Box(
modifier = Modifier
.size(60.dp)
.clip(RectangleWithCircularCutOut(cornerRadiusInDp = 4.dp, cutCornerRadiusInDp = 16.dp))
.background(Color.Blue)
)
}
@Preview
@Composable
fun PreviewCustomShapeDemo() {
CustomShapeDemo()
}
In this example, we create a Box
with a size of 60dp, clip it to our RectangleWithCircularCutOut
shape, and set its background color to blue.
Explanation of the Path Drawing
- Starting Point: We start by moving to the initial point
(cornerRadius, 0f)
. - Top-Right Corner Arc: We draw an arc from the top-right corner using the
arcTo
function. - Bottom-Right Corner: Draw a line to the bottom-right corner.
- Circular Cutout Arc: Draw an arc to create the circular cutout.
- Bottom-Left Corner: Draw a line to the bottom-left corner.
- Bottom-Left Corner Arc: Draw an arc for the bottom-left corner.
- Top-Left Corner: Draw a line to the top-left corner.
- Top-Left Corner Arc: Draw an arc for the top-left corner.
- Closing the Path: Close the path to complete the shape.
Conclusion
Creating custom shapes in Jetpack Compose can seem daunting, but with a solid understanding of the Path
and Shape
APIs, you can create intricate and unique UI components. The RectangleWithCircularCutOut
shape demonstrates how to combine lines and arcs to form a custom shape that can be reused across your Compose application.
Experiment with different corner and cutout sizes to create various visual effects and enhance your app’s UI design. Happy coding!