지금까지는 모서리가 둥근 경우만 만들어봤다.
그러나 컷팅된 모양이나 안쪽으로 둥근 모서리 스타일을 만들고 싶다면?
원하는 모서리 스타일을 설정할 수 있도록 enum class를 만들었다.
enum class CornerStyle {
ROUNDED,
INNER_ROUNDED,
CUT
}
둥근 모서리일 때는 cornerRadius가 0일 경우는 뾰족한 형태로 그려지기 때문에 같은 코드를 공유했다.
그러나 CUT이나 Inner_rounded 같은 경우는 arc 각도나 시작 포인트가 다르기 때문에 모서리 스타일 별로 함수를 구분하였다.
class RectangleShape(
private val cornerStyle: CornerStyle,
private val topStart: CornerSize,
private val topEnd: CornerSize,
private val bottomEnd: CornerSize,
private val bottomStart: CornerSize,
) : Shape {
override fun createOutline(
size: Size,
layoutDirection: LayoutDirection,
density: Density,
): Outline {
val path = when (cornerStyle) {
CornerStyle.ROUNDED -> {
drawRoundedRectangleShape(
size = size,
topStart = topStart.toPx(size, density),
topEnd = topEnd.toPx(size, density),
bottomStart = bottomStart.toPx(size, density),
bottomEnd = bottomEnd.toPx(size, density),
)
}
CornerStyle.INNER_ROUNDED -> {
drawInnerRoundedRectangleShape(
size = size,
topStart = topStart.toPx(size, density),
topEnd = topEnd.toPx(size, density),
bottomStart = bottomStart.toPx(size, density),
bottomEnd = bottomEnd.toPx(size, density),
)
}
CornerStyle.CUT -> {
drawCutRectangleShape(
size = size,
topStart = topStart.toPx(size, density),
topEnd = topEnd.toPx(size, density),
bottomStart = bottomStart.toPx(size, density),
bottomEnd = bottomEnd.toPx(size, density),
)
}
}
return Outline.Generic(path)
}
}
InnerCorner같은 경우는 바깥에서 그려야 하고 원의 뾰족한 모서리 지점에서 원을 그리면 그게 cornerRadius만큼 자른 게 되므로 모서리 지점이 원의 중심이 된다.
그리고 arc를 그릴 때 반시계방향으로 그려야 시작과 끝 지점이 유지가 된다.
sweepAngle을 음수값으로 지정하면 반시계방향으로 회전한다.
arcTo(
rect = Rect(
offset = Offset(
-topStart,
-topStart
),
size = Size(topStart * 2, topStart * 2)
),
startAngleDegrees = 90f,
sweepAngleDegrees = -90f,
forceMoveTo = false
)
모서리의 각도를 이용해 sin, cos으로 빼고 더해주는 방식으로 시작과 끝 지점을 계산해줬다.
lineTo(
width - skewedWidth + bottomEnd * cos(topRightAngle),
height - bottomEnd * sin(topRightAngle)
)
이전과 현재, 다음 지점의 좌표값을 이용하여 이전~현재 지점 or 현재~다음 지점의 거리와 cornerRadius의 길이 비율로 시작과 끝 지점을 계산해줬다.
val startPoint = if (i % 2 == 0) {
Point(
x - prevDx * outerCornerSize / polygonDist,
y - prevDy * outerCornerSize / polygonDist
)
} else {
Point(
x - prevDx * innerCornerSize / polygonDist,
y - prevDy * innerCornerSize / polygonDist
)
}
val endPoint = if (i % 2 == 0) {
Point(
x + nextDx * outerCornerSize / polygonDist,
y + nextDy * outerCornerSize / polygonDist
)
} else {
Point(
x + nextDx * innerCornerSize / polygonDist,
y + nextDy * innerCornerSize / polygonDist
)
}
| Rounded | Inner_rounded | Cut |
|---|---|---|
![]() | ![]() | ![]() |
최종결과에 있던 사다리꼴 코드이다.
private fun drawInnerRoundedTrapezoidShape(
size: Size,
startSkewed: Float,
endSkewed: Float,
topStart: Float,
topEnd: Float,
bottomEnd: Float,
bottomStart: Float,
): Path {
val path = Path()
val width = size.width
val height = size.height
val startSkewedWidth = width * startSkewed
val endSkewedWidth = width * endSkewed
val bottomStartAngle = atan(height / startSkewedWidth)
val bottomEndAngle = atan(height / endSkewedWidth)
val bottomStartAngleDegree = bottomStartAngle.toDegree()
val bottomEndAngleDegree = bottomEndAngle.toDegree()
path.apply {
arcTo(
rect = Rect(
offset = Offset(
startSkewedWidth - topStart,
-topStart
),
size = Size(topStart * 2, topStart * 2),
),
startAngleDegrees = 180f - bottomStartAngleDegree,
sweepAngleDegrees = bottomStartAngleDegree - 180f,
forceMoveTo = false
)
arcTo(
rect = Rect(
offset = Offset(
width - endSkewedWidth - topEnd,
-topEnd
),
size = Size(topEnd * 2, topEnd * 2),
),
startAngleDegrees = 180f,
sweepAngleDegrees = bottomEndAngleDegree - 180f,
forceMoveTo = false
)
arcTo(
rect = Rect(
offset = Offset(
width - bottomEnd,
height - bottomEnd
),
size = Size(bottomEnd * 2, bottomEnd * 2),
),
startAngleDegrees = 180f + bottomEndAngleDegree,
sweepAngleDegrees = -bottomEndAngleDegree,
forceMoveTo = false
)
arcTo(
rect = Rect(
offset = Offset(
-bottomStart,
height - bottomStart
),
size = Size(bottomStart * 2, bottomStart * 2),
),
startAngleDegrees = 0f,
sweepAngleDegrees = -bottomStartAngleDegree,
forceMoveTo = false
)
close()
}
return path
}
최종결과에 있던 마름모 코드이다.
private fun drawCutRhombusShape(
size: Size,
top: Float,
start: Float,
end: Float,
bottom: Float,
): Path {
val path = Path()
val width = size.width
val height = size.height
val centerX = width / 2
val centerY = height / 2
val dist = sqrt(centerX * centerX + centerY * centerY)
path.apply {
moveTo(
centerX - top * centerX / dist,
0f + top * centerY / dist
)
lineTo(
centerX + top * centerX / dist,
0f + top * centerY / dist
)
lineTo(
width - end * centerX / dist,
centerY - top * centerY / dist
)
lineTo(
width - end * centerX / dist,
centerY + end * centerY / dist
)
lineTo(
centerX + bottom * centerX / dist,
height - bottom * centerY / dist
)
lineTo(
centerX - bottom * centerX / dist,
height - bottom * centerY / dist
)
lineTo(
start * centerX / dist,
centerY + start * centerY / dist
)
lineTo(
start * centerX / dist,
centerY - start * centerY / dist
)
close()
}
return path
}