Skip to content

Instantly share code, notes, and snippets.

@SmartToolFactory
Created September 3, 2022 08:42
Show Gist options
  • Save SmartToolFactory/d34a51fe9cf9912b26133768ebe092f1 to your computer and use it in GitHub Desktop.
Save SmartToolFactory/d34a51fe9cf9912b26133768ebe092f1 to your computer and use it in GitHub Desktop.
Samples for clipping or cutting Composables
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CutCornerShape
import androidx.compose.material.BottomNavigation
import androidx.compose.material.BottomNavigationItem
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.*
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.graphics.drawscope.withTransform
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.imageResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Composable
fun ClipComposables() {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceEvenly
) {
val imageBitmap = ImageBitmap.imageResource(id = R.drawable.squircle)
Box(modifier = Modifier
.size(150.dp)
.drawWithLayer {
// Destination
drawContent()
// Source
drawImage(
image = imageBitmap,
dstSize = IntSize(width = size.width.toInt(), height = size.height.toInt()),
blendMode = BlendMode.DstIn
)
}
) {
Box(
modifier = Modifier
.size(150.dp)
.clickable { }
.background(MaterialTheme.colorScheme.inversePrimary),
contentAlignment = Alignment.Center
) {
Text(text = "Squircle", fontSize = 20.sp)
}
}
Box(modifier = Modifier
.size(150.dp)
.drawWithLayer {
// Destination
drawContent()
// Source
drawImage(
image = imageBitmap,
dstSize = IntSize(width = size.width.toInt(), height = size.height.toInt()),
blendMode = BlendMode.DstIn
)
}
) {
Image(
painterResource(id = R.drawable.squirtle),
modifier = Modifier
.size(150.dp),
contentScale = ContentScale.Crop,
contentDescription = ""
)
}
}
}
@Composable
fun BottomBarWithCutOutShape() {
val density = LocalDensity.current
val shapeSize = density.run { 70.dp.toPx() }
val cutCornerShape = CutCornerShape(50)
val outline = cutCornerShape.createOutline(
Size(shapeSize, shapeSize),
LocalLayoutDirection.current,
density
)
val icons =
listOf(Icons.Filled.Home, Icons.Filled.Map, Icons.Filled.Settings, Icons.Filled.LocationOn)
Box(
modifier = Modifier.fillMaxWidth()
) {
BottomNavigation(
modifier = Modifier
.drawWithLayer {
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
val width = size.width
val outlineWidth = outline.bounds.width
val outlineHeight = outline.bounds.height
// Destination
drawContent()
// Source
withTransform(
{
translate(
left = (width - outlineWidth) / 2,
top = -outlineHeight / 2
)
}
) {
drawOutline(
outline = outline,
color = Color.Transparent,
blendMode = BlendMode.Clear
)
}
restoreToCount(checkPoint)
}
},
backgroundColor = Color.White
) {
var selectedIndex by remember { mutableStateOf(0) }
icons.forEachIndexed { index, imageVector: ImageVector ->
if (index == 2) {
Spacer(modifier = Modifier.weight(1f))
BottomNavigationItem(
icon = { Icon(imageVector, contentDescription = null) },
label = null,
selected = selectedIndex == index,
onClick = {
selectedIndex = index
}
)
} else {
BottomNavigationItem(
icon = { Icon(imageVector, contentDescription = null) },
label = null,
selected = selectedIndex == index,
onClick = {
selectedIndex = index
}
)
}
}
}
// This is size fo BottomNavigationItem
val bottomNavigationHeight = LocalDensity.current.run { 56.dp.roundToPx() }
FloatingActionButton(
modifier = Modifier
.align(Alignment.TopCenter)
.offset {
IntOffset(0, -bottomNavigationHeight / 2)
},
shape = cutCornerShape,
onClick = {}
) {
Icon(imageVector = Icons.Default.Add, contentDescription = null)
}
}
}
@Composable
fun WhoAteMyButton() {
val circleSize = LocalDensity.current.run { 100.dp.toPx() }
Box(
modifier = Modifier
.fillMaxWidth()
.drawWithLayer {
// Destination
drawContent()
// Source
drawCircle(
center = Offset(0f, 10f),
radius = circleSize,
blendMode = BlendMode.SrcOut,
color = Color.Transparent
)
}
) {
Button(
modifier = Modifier
.padding(horizontal = 10.dp)
.fillMaxWidth(),
onClick = { /*TODO*/ }) {
Text("Hello World")
}
}
}
fun Modifier.drawWithLayer(block: ContentDrawScope.() -> Unit) = this.then(
Modifier.drawWithContent {
drawWithLayer {
block()
}
}
)
fun ContentDrawScope.drawWithLayer(block: ContentDrawScope.() -> Unit) {
with(drawContext.canvas.nativeCanvas) {
val checkPoint = saveLayer(null, null)
block()
restoreToCount(checkPoint)
}
}
@SmartToolFactory
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment