Jetpack Compose: Shaping Your UI with Clipping and Masking

Last updated Dec 24, 2024

Compose is a modern toolkit for building native UI on Android. It simplifies and accelerates UI development with less code, powerful tools, and an intuitive Kotlin API. Here are some tips to help you create delightful UIs with Compose

How to Create Stacked Avatars in Jetpack Compose

Compose stack avatar

Jetpack Compose is a modern toolkit for building user interfaces on Android, focusing on UI design through techniques such as clipping and masking. It utilizes shapes and paths to create visually appealing components, enhanced by various modifiers that allow developers to customize their applications. This framework is integral to Android and mobile development, facilitating the creation of intuitive user interfaces that improve the overall user experience. Developers leverage Kotlin programming to implement these features effectively

Embrace Composable Functions

Everything is a Composable: Build your UI using composable functions, the fundamental building blocks of Compose. This promotes modularity, reusability, and easier state management.

State Management with remember: Use remember to store and update UI state within composables. This ensures that your UI updates correctly when the state changes.

Lazy Compositions: Improve performance by using lazy to defer the composition of complex or expensive UI elements until they are actually needed.

2. Leverage Compose Modifiers

Visual Enhancements: Apply modifiers like padding, size, background, border, and clip to customize the appearance of your UI elements.

Layout Control: Use modifiers like width, height, fillMaxWidth, wrapContentSize, and aspectRatio to control the size and layout of your components.

Interaction Handling: Utilize modifiers like clickable, draggable, and pointerInput to handle user interactions with your UI.

3. Material Design 3 Support

Modern Look and Feel: Compose seamlessly integrates with Material Design 3, providing access to a rich set of pre-built components and styles.

Dynamic Theming: Easily adapt your UI to light and dark mode using Compose's built-in theming support.

Accessibility: Compose makes it easy to build accessible UIs that are usable by people with disabilities.

4. Advanced Techniques

Custom Layouts: Create custom layouts using Layout and SubcomposeLayout to achieve complex UI arrangements.

Animations and Transitions: Utilize Compose's animation system to create smooth and engaging user experiences.

Constraints: Define constraints on your UI elements using modifiers like constrainAs to control their size and position within the layout.

5. Best Practices

Keep Composable Functions Small and Focused: Break down complex UI into smaller, more manageable composable functions.

Use a Consistent Naming Convention: Choose clear and descriptive names for your composables and modifiers.

Write Tests: Write unit tests for your composable functions to ensure their correctness and maintainability

 

 

Clipping and Masking in Jetpack Compose

Discover how to use clipping and masking to shape your UI, control content visibility, and create unique and engaging user experiences

Clipping and masking are powerful techniques in Jetpack Compose that allow you to control the visible portions of your UI elements, creating visually interesting and unique effects.

 

Compose Custom shapes - Polygone

Clipping

Clipping involves removing parts of a composable based on a defined shape. Only the content within the shape is displayed, while the rest is hidden

How to Clip in Compose:

  1. Create a Shape: You can use predefined shapes like CircleShape, RoundedCornerShape, or create custom shapes using the Shape interface.

  2. Apply the clip Modifier: Apply the clip modifier to the composable you want to clip. Pass the shape you created as an argument

 

@Preview

@Composable

fun CircularShape() {

 

   Column {

       Text("Circular Shape", color = Color.Red, fontWeight = FontWeight.Bold)

       Image(

           painter = painterResource(

               R.drawable.dog

           ),

           contentDescription = "Custom shape",

           contentScale = ContentScale.Crop,

           modifier = Modifier

               .size(200.dp)

               .clip(CircleShape)

       )

   }

}

 

Creating a Custom Shapes in compose

Oval Shape

 

@Preview

@Composable

fun SquashedOval() {

 

   Column {

       Text("Circular Oval Shape", color = Color.Red, fontWeight = FontWeight.Bold)

       Image(

           painter = painterResource(

               R.drawable.dog

           ),

           contentDescription = "Custom shape",

           modifier = Modifier

               .size(100.dp)

               .clip(SquashedOvelShape())

       )

 

   }

}

 

Implementing Polygon Shapes in Jetpack Compose

@Preview

@Composable

fun CustomPolygonShape() {

 

   //polygonShape.toPath().asComposePath()

   Column {

       Text("Custom Polygon Shape", color = Color.Red, fontWeight = FontWeight.Bold)

       Image(

           painter = painterResource(

               R.drawable.dog

           ),

           contentDescription = "Custom shape",

           contentScale = ContentScale.Crop,

           modifier = Modifier

               .size(200.dp)

               .clip(RoundedPolygonShape(getPolygon(0.4f)))

 

       )

       Image(

           painter = painterResource(

               R.drawable.dog

           ),

           contentDescription = "Custom shape",

           contentScale = ContentScale.Crop,

           modifier = Modifier

               .size(200.dp)

               .clip(RoundedPolygonShape(getPolygon(1.4f)))

 

       )

   }

}

 

We can indeed create various custom shapes for clipping in Compose. However, how would we approach a scenario where we need to display multiple user profile avatars overlapping each other (essentially stacking them)? Each avatar should be individually clipped into a circular shape

Compose stack avatar

 

The Stacked Avatar Effect in Jetpack Compose

How can we do this?

First create the avatar composable and clip the composable with a circle shape,Then using the drawWithContent modifier

 we first call drawContent, which will draw the avatar itself then we draw a circle shape around the content

That will be used to clip the outline away from each avatar. We use a stroke style and draw the circle at the full size of the canvas with the color blue.

The next step is to stack the avatars on top of one another. We draw an avatar for each in our list of avatars with a particular stroke then we will set the offset of each avatar to increase the X offset as each item is iterated over.

Finally we achieved our requirement of a nice stack of avatars

 

@Preview

@Composable

private fun StackAcatars() {

   var size= Size(90.0f,90.0f)

   Box(modifier = Modifier.fillMaxWidth())

   {

       var offset=0.dp

       for(k in 0..6)

       {

           Avatar(strokeWidth = 4.0f, modifier = Modifier.size(width = 120.dp,120.dp).offset(offset)) {

               Image(painter = painterResource(R.drawable.dog),

                   contentScale = ContentScale.Crop,

                   contentDescription = "")

           }

           offset+= 60.dp

       }

   } 

}

@Composable

fun Avatar(

   strokeWidth:Float,

   modifier:Modifier=Modifier,

   content:@Composable () -> Unit

) {

   Val stroke= remember (strokeWidth) {

     Stroke(width = strokeWidth)

   }

   Box(modifier= modifier

       .drawWithContent {

           drawContent()

           drawCircle(

               Color.Blue,

               size.minDimension / 2,

               size.center,

               style = stroke

           )

       }

       .clip(CircleShape)

   ){

       content()

   }

}

 

However, the black border is still present, we want the gradient background to be able to render between avatars by set the blend mode. The clear blend mode clears the pixels covered by the source in the destination

 

drawCircle(

   Color.Yellow,

   size.minDimension / 2,

   size.center,

   style = stroke,

   blendMode = BlendMode.Clear

)

 

 

Masking

Masking is similar to clipping, but it allows for more control over the visibility of the content. You can use a mask to reveal or hide parts of a composable based on the opacity of the mask.

How to Mask in Compose:

  1. Create a Mask: You can use a Path or a Bitmap as a mask.

  2. Apply the mask Modifier: Apply the mask modifier to the composable you want to mask. Pass the mask you created as an argument

 

@Composable

fun MaskedImage() {

    val maskPath = Path().apply {

        addRect(0f, 0f, 100f, 100f, Path.Direction.CW)

        addCircle(50f, 50f, 30f, Path.Direction.CW) // Create a circular hole in the mask

    }

 

    Image(

        painter = painterResource(id = R.drawable.your_image),

        contentDescription = null,

        modifier = Modifier

            .size(100.dp)

            .mask(maskPath)

    )

}

 

Combine Clipping and Masking: You can combine clipping and masking to achieve more complex effects.

Custom Shapes: Create custom shapes using the Shape interface to achieve unique clipping or masking effects.

Animations: Animate clipping or masking to create dynamic and engaging UI elements

 

Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

970 Views