I've attempted to create a hexagon shape in jetpack compose but could only achieve pointed ends, tried several methods with quadraticBezierTo and arcs but couldn't achieve a result with curved ends.
I've tried to look on the web but not quite what i wanted, If you have an idea how to achieve it, thanks for answering to this post.
Here's the current code without curved ends:
class HexagonShape : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density
): Outline {
return Outline.Generic(
path = drawCustomHexagonPath(size)
)
}
}
private fun drawCustomHexagonPath(size: Size): Path {
return Path().apply {
val radius = min(size.width / 2f, size.height / 2f)
val triangleHeight = (sqrt(3.0) * radius / 2)
val centerX = size.width / 2
val centerY = size.height / 2
moveTo(x = centerX, y = centerY + radius)
lineTo(x = (centerX - triangleHeight).toFloat(), y = centerY + radius / 2)
lineTo(x = (centerX - triangleHeight).toFloat(), y = centerY - radius / 2)
lineTo(x = centerX, y = centerY - radius)
lineTo(x = (centerX + triangleHeight).toFloat(), y = centerY - radius / 2)
lineTo(x = (centerX + triangleHeight).toFloat(), y = centerY + radius / 2)
close()
}
}
@Composable
fun Hexagon(modifier: Modifier = Modifier) {
Box(
modifier = modifier
.clip(HexagonShape())
.background(md_theme_light_inversePrimary)
.width(500.dp)
.height(500.dp),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = R.drawable.app_bg),
contentDescription = "My Hexagon Image",
contentScale = ContentScale.Crop,
modifier = Modifier
.wrapContentSize()
.background(color = Color.Cyan)
)
}
}
You can use pathEffect = PathEffect.cornerPathEffect(cornerRadius)
@Composable
private fun DrawPolygonPath() {
var sides by remember { mutableStateOf(3f) }
var cornerRadius by remember { mutableStateOf(1f) }
Canvas(modifier = canvasModifier) {
val canvasWidth = size.width
val canvasHeight = size.height
val cx = canvasWidth / 2
val cy = canvasHeight / 2
val radius = (canvasHeight - 20.dp.toPx()) / 2
val path = createPolygonPath(cx, cy, sides.roundToInt(), radius)
drawPath(
color = Color.Red,
path = path,
style = Stroke(
width = 4.dp.toPx(),
pathEffect = PathEffect.cornerPathEffect(cornerRadius)
)
)
}
Column(modifier = Modifier.padding(horizontal = 20.dp)) {
Text(text = "Sides ${sides.roundToInt()}")
Slider(
value = sides,
onValueChange = { sides = it },
valueRange = 3f..12f,
steps = 10
)
Text(text = "CornerRadius ${cornerRadius.roundToInt()}")
Slider(
value = cornerRadius,
onValueChange = { cornerRadius = it },
valueRange = 0f..50f,
)
}
}
and polygon function
fun createPolygonPath(cx: Float, cy: Float, sides: Int, radius: Float): Path {
val angle = 2.0 * Math.PI / sides
return Path().apply {
moveTo(
cx + (radius * cos(0.0)).toFloat(),
cy + (radius * sin(0.0)).toFloat()
)
for (i in 1 until sides) {
lineTo(
cx + (radius * cos(angle * i)).toFloat(),
cy + (radius * sin(angle * i)).toFloat()
)
}
close()
}
}
You can get ImageBitmap
from Coil and you can draw it with BlendMode
as like this
@Preview
@Composable
fun ImageTest() {
Column(
modifier = Modifier.fillMaxSize().padding(32.dp)
) {
val imageBitmap = ImageBitmap.imageResource(R.drawable.avatar_1_raster)
Box(
Modifier
.size(200.dp)
.border(2.dp, Color.Green)
.graphicsLayer {
compositingStrategy = CompositingStrategy.Offscreen
}
.drawWithCache {
val canvasWidth = size.width
val canvasHeight = size.height
val cx = canvasWidth / 2
val cy = canvasHeight / 2
val radius = canvasHeight / 4
val path = createPolygonPath(cx, cy, 6, radius)
onDrawWithContent {
// Source
drawPath(
path = path,
color = Color.Red,
style = Stroke(
width = canvasHeight / 2,
pathEffect = PathEffect.cornerPathEffect(15f)
)
)
drawCircle(
color = Color.Red,
radius = 10f
)
// Destination
drawImage(
image = imageBitmap,
dstSize = IntSize(size.width.toInt(), size.height.toInt()),
blendMode = BlendMode.SrcIn
)
}
}
)
}
}