ShapeableView in Jetpack Compose. Part 1

Rasul Aghakishiyev
ProAndroidDev
Published in
4 min readFeb 6, 2021

--

In the previous story, I talked about how to create a different types of shapes in Jetpack Compose.

In Material Components we have a widget called ShapeableImageView. Today we will create the Composable View that allows having a different type of corners

In Material Components we have a widget called ShapeableImageView. It allows you to change the shape of your image to circle, diamond, and so on. And also, using this widget you can set a corner radius to your ImageView. Today we will create the Composable View that allows having a different type of corners

First, we need to composable function:

@Composable
fun ShapeableView() {
Box(
modifier = Modifier
.size(150.dp)
)
}

It’s a simple composable view without any logic.

Corners will be three types: rounded, cut, and none if our view corner is a straight line

For this create enum class called CornerType

enum class CornerType {
CUT,
ROUNDED,
NONE
}

In this lesson, we will just use the CUT corner type, but in the next lesson, we will add other corner types.

Also, we need a class that keeps the information about corner type and size. By default, our shapes don’t have corners.

data class ShapeCorner(
val type: CornerType = CornerType.NONE,
val size: Dp = 0.dp
)

In case every corner of our shape can be modified, we should have a data class that will hold information about every corner

data class ShapeDecoration(
val topStart: ShapeCorner = ShapeCorner(),
val topEnd: ShapeCorner = ShapeCorner(),
val bottomEnd: ShapeCorner = ShapeCorner(),
val bottomStart: ShapeCorner = ShapeCorner()
)

So the initial setup is done. Now we will write our shape render logic

  1. Create a class that extends Shape interface with shapeDecoration parameter in its constructor
class Shapeable(private val decoration: ShapeDecoration) : Shape {
override fun createOutline(size: Size, density: Density): Outline {
/**
* There will be our code
*/
}
}

We will use Path for creating custom shapes. To do this we must get the size of every corner in pixels because all calculations below will be in pixels.

To convert Dp into pixels we just multiply our value by the current density

val topStartSize = decoration.topStart.size.value * density.density
val topEndSize = decoration.topEnd.size.value * density.density
val bottomEndSize = decoration.bottomEnd.size.value * density.density
val bottomStartSize = decoration.bottomStart.size.value * density.density

Next we will write logic for CUT corner type:

Our drawing path will be like this

So to make cut corner, we should cut our shape sides. For example, for the top cut corner, we must cut topStartSize from our top and left sides. The code below shows how to do this.

Our first point has coordinates x= 0f and y = topStartSize. To cut left corner we line to position x= topStartSize and y = 0f.

moveTo(0f, topStartSize)
lineTo(topStartSize, 0f)

Then we will cut our bottom right corner :

lineTo(size.width - topEndSize, 0f)
lineTo(size.width, topEndSize)

And finally our bottom left corner:

lineTo(size.width, size.height - bottomEndSize)
lineTo(size.width - bottomEndSize, size.height)

That’s it. We just created a shape that will cut our corner as we want. Let’s try it.

@Preview
@Composable
fun ShapeableViewTest() {
Box(
modifier = Modifier
.size(150.dp)
.clip(
Shapeable(
decoration = ShapeDecoration(
topStart = ShapeCorner(
type = CornerType.CUT,
size = 50.dp
),
),
)
)
.background(Color.Gray)
) {
}
}

The output will be something like this:

Also we can cut two corners of our shape:

@Preview
@Composable
fun ShapeableViewTest() {
Box(
modifier = Modifier
.size(150.dp)
.clip(
Shapeable(
decoration = ShapeDecoration(
topStart = ShapeCorner(
type = CornerType.CUT,
size = 50.dp
),
bottomEnd = ShapeCorner(
type = CornerType.CUT,
size = 50.dp
),

),
)
)
.background(Color.Gray)
)
}

And the output now will be like this

Or we can make a diamond shape:

@Preview
@Composable
fun ShapeableViewTest() {
Box(
modifier = Modifier
.size(150.dp)
.clip(
Shapeable(
decoration = ShapeDecoration(
topStart = ShapeCorner(
type = CornerType.CUT,
size = 75.dp
),
topEnd = ShapeCorner(
type = CornerType.CUT,
size = 75.dp
),
bottomStart = ShapeCorner(
type = CornerType.CUT,
size = 75.dp
),
bottomEnd = ShapeCorner(
type = CornerType.CUT,
size = 75.dp
),
),
)
)
.background(Color.Gray)
)
}

Conclusion

Today we created a Shapeable component that allows you to cut every corner. I hope it will help you to create an awesome UI and make your development easier.

Feel free to follow me on Twitter. Also, don’t hesitate to ask questions

Thanks for reading, and see you later!

--

--

Android Software Engineer. Interested in mobile development. In love with Jetpack Compose