1

So I need to implement similar shape in Jetpack Compose

enter image description here

I'm trying it with the following code but for now with no luck:

fun clippedCardShape(clipRadius: Float = 30f): Shape { return object : Shape { override fun createOutline( size: androidx.compose.ui.geometry.Size, layoutDirection: LayoutDirection, density: Density ): Outline { val path = Path().apply { // Top line lineTo(size.width, 0f) // Right side clipping lineTo(size.width, size.height / 2 - clipRadius) arcTo( rect = Rect( left = size.width - clipRadius * 2, top = size.height / 2 - clipRadius, right = size.width, bottom = size.height / 2 + clipRadius ), startAngleDegrees = 270f, sweepAngleDegrees = -180f, forceMoveTo = false ) lineTo(size.width, size.height) // Bottom line lineTo(0f, size.height) // Left side clipping lineTo(0f, size.height / 2 + clipRadius) arcTo( rect = Rect( left = 0f, top = size.height / 2 - clipRadius, right = clipRadius * 2, bottom = size.height / 2 + clipRadius ), startAngleDegrees = 90f, sweepAngleDegrees = 180f, forceMoveTo = false ) lineTo(0f, 0f) close() } return Outline.Generic(path) } } } 

The code tries to add clipping at the middle, but it should be almost at the top of the shape like on the example image.

Result:

enter image description here

So I would like to see a working example from someone who knows how to deal with it so I could learn it and figure out the logic and math how implement to similar shapes for different cases.

Update

a little progress but still with issues:

enter image description here

val clipRadius = 20f // Radius of the left and right clips val path = Path().apply { // Move to the top-left corner moveTo(0f, 0f) // Top-left corner lineTo(0f, 0f) // Left clip arc arcTo( rect = Rect( left = 0f, top = (size.height / 2) - clipRadius, right = clipRadius * 2, bottom = (size.height / 2) + clipRadius ), startAngleDegrees = 270f, sweepAngleDegrees = 180f, forceMoveTo = false ) // Bottom-left corner lineTo(0f, size.height) // Bottom-left to bottom-right lineTo(size.width, size.height) // Right clip arc arcTo( rect = Rect( left = size.width - (clipRadius * 2), top = (size.height / 2) - clipRadius, right = size.width, bottom = (size.height / 2) + clipRadius ), startAngleDegrees = 90f, sweepAngleDegrees = 180f, forceMoveTo = false ) // Bottom-right corner lineTo(size.width, 0f) // Close the shape close() } return Outline.Generic(path) 
3
  • Do you need this specifically be a Shape or you want to clip content as in the image? If you want to clip your content as in image you can use BlendModes or clip with Path. Commented Jan 22 at 12:38
  • This can be done with using BlendModes as in this answer or adding two ovals to a Path and slipping with path operations. Commented Jan 22 at 12:39
  • @Thracian it should be like a shape, for example just like the following Ticket shape github.com/DawinderGill/CustomShapes-JetpackCompose/raw/main/… from github.com/DawinderGill/… Commented Jan 22 at 12:40

1 Answer 1

3

You can do it like this, using addOval:

@Composable fun ClippedCardShape() { Scaffold { paddingValues -> LazyColumn(Modifier.padding(paddingValues)) { items(10) { Image( modifier = Modifier .fillMaxWidth() .clip(clippedCardShape()), painter = painterResource(R.drawable.ic_launcher_background), contentDescription = null, contentScale = ContentScale.FillBounds ) } } } } fun clippedCardShape(clipRadius: Float = 30f, clipPosition: Float = 0.2f): Shape = object : Shape { override fun createOutline( size: Size, layoutDirection: LayoutDirection, density: Density ): Outline { val fullPath = Path().apply { addRect( Rect( left = 0f, top = 0f, right = size.width, bottom = size.height ) ) } val path = Path().apply { addOval( oval = Rect( center = Offset(x = 0f, y = size.height * clipPosition), radius = clipRadius ), ) addOval( oval = Rect( center = Offset(x = size.width, y = size.height * clipPosition), radius = clipRadius ), ) } return Outline.Generic(fullPath - path) } } 
Sign up to request clarification or add additional context in comments.

3 Comments

thanks but it seems it kind of makes oval as shape but all other content are clipped, so basically inverted
@user924 I've updated the answer, you can completely paint over all the content and subtract those two semicircles.
Wow! Subtract trick is awesome, thanks so much this solution!

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.