
ShapeableView in Jetpack Compose. Part 2
In the post before we learned how to create Shapes with cut corners. Today we will continue and add some new functionality such as rounded corners to our ShapeableView.
In previous examples, we had two corner types which were CUT and NONE. Let’s add ROUNDED corner type to our enum class.
enum class CornerType {
CUT,
ROUNDED,
NONE
}
Also, we add when expression to control which corner type selected for every corner:
when (decoration.topStart.type) {
CornerType.CUT, CornerType.NONE -> {
moveTo(0f, topStartSize)
lineTo(topStartSize, 0f)
}
CornerType.ROUNDED_IN -> {
// There will be our code
}
}
We will use arcTo to draw round corners.
fun arcTo(
rect: Rect,
startAngleDegrees: Float,
sweepAngleDegrees: Float,
forceMoveTo: Boolean
)
It takes 4 arguments. Let’s look at each of them
rect
— The bounds of oval defining shape and size of the arc This value cannot be null
.
startAngleDegrees
— Starting angle (in degrees) where the arc begins.
sweepAngle
— Sweep angle (in degrees) measured clockwise, treated mod 360.
forceMoveTo
—if the argument is true, starts a new subpath consisting of an arc segment.
So basically we draw the rect in our shape in with given offset and size.
Before we start draw our arc, you should know how to degrees placed.

As you can see 0 degree is in the right center, not in the top center. And it move clockwise.
Now let draw our first round corner. We will start from topStart corner. First we need create Rect where our circle will be placed.
Our rect must placed at top start corner with size equal to our top start corner size:
arcTo(
rect = Rect(
Offset(0f, 0f),
Size(topStartSize, topStartSize)
),
startAngleDegrees = 180f,
sweepAngleDegrees = 90f,
forceMoveTo = false
)
Rect constructor takes two parameters:
offset: Offset
size: Size
offset — The top left corner of the rect
size — The size of the rect

So our arc starts at 180 degree and move 90 degrees clockwise.
And the result will be like this:

I will not dive deeply into other corners creation, just place the code for every corner below
TOP_END
Our rect must placed at top end corner with size equal to our top end corner size:
arcTo(
rect = Rect(
Offset(x = size.width - topEndSize, y = 0f),
Size(width = topEndSize,height = topEndSize)
),
startAngleDegrees = 270f,
sweepAngleDegrees = 90f,
forceMoveTo = false
)
BOTTOM_END
Our rect must placed at bottom end corner with size equal to our bottom end corner size:
arcTo(
Rect(
Offset(x = size.width - bottomEndSize, y = size.height - bottomEndSize),
Size(width = bottomEndSize,height = bottomEndSize)
),
0f,
90f,
forceMoveTo = false
)
BOTTOM_START
Our rect must placed at bottom start corner with size equal to our bottom start corner size:
arcTo(
rect = Rect(
Offset(x = 0f, y = size.height - bottomStartSize),
Size(width = bottomStartSize,height = bottomStartSize)
),
startAngleDegrees = 90f,
sweepAngleDegrees = 90f,
forceMoveTo = false
)
Now it’s time to combine cut and rounded corner types and see result
For example:
@Composable
fun ShapeableView() {
Box(
modifier = Modifier
.size(150.dp)
.clip(
Shapeable(
decoration = ShapeDecoration(
topStart = ShapeCorner(
type = CornerType.CUT,
size = 25.dp
),
topEnd = ShapeCorner(
type = CornerType.ROUNDED_IN,
size = 75.dp
),
bottomEnd = ShapeCorner(
type = CornerType.CUT,
size = 25.dp
),
bottomStart = ShapeCorner(
type = CornerType.ROUNDED_IN,
size = 75.dp
),
),
)
)
.background(Color.Gray)
)
}

Since we created a shape, it means we can apply this corners to any shape.
For example Image:
@Composable
fun ShapeableImage() {
Image(
imageResource(id = R.drawable.android2), contentScale = ContentScale.Crop,
modifier = Modifier
.size(150.dp)
.clip(
Shapeable(
decoration = ShapeDecoration(
topStart = ShapeCorner(
type = CornerType.CUT,
size = 55.dp
),
bottomEnd = ShapeCorner(
type = CornerType.ROUNDED_IN,
size = 55.dp
),
),
)
)
)
}

Conclusion
Today we created a shape that the same functionality as ShapeableImageView from Material Components which gives you the ability to modify every corner of the view. The main difference between them is that you can clip any composable view with this shape,
I hope it will help to make your life easier and the development process more enjoyable
Feel free to follow me on Twitter. Also, don’t hesitate to ask questions
Thanks for reading, and see you later!