๐ง ๋ฐฐ๊ฒฝ
์๋๋ก์ด๋๋ฅผ ์ฒ์ ์ ํ์ ๋๋ถํฐ ํ ์คํธ, ๋ฒํผ์ ๋ฅ๊ธ๊ฒ ๋ง๋ค๊ธฐ ์ํด์ ์๋ง์ XML ํ์ผ์ ์์ฑํด์๋ค.
๊ฝค ๋ง์ ํ๋ก์ ํธ๋ฅผ ํด์์ง๋ง, ์ฝ๋๊ฐ์ ๋ฃ๊ธฐ ์ํด์ ๊ธฐ๊ณ์ ์ผ๋ก XML Shape๋ฅผ ์ฐ๊ณ ์์๋ค.
ํ์ฌ ์ ์ฌ์ ์ถ์๋ฅผ ์๋๊ณ ํ๋ก์ ํธ ์ ๋ฆฌ๋ฅผ ํ๊ณ ์์๋๋ฐ, ์ ๋ง ๋ณด๊ธฐ ์ซ์ ์ ๋์ shape XML๋ค์ด ์์๋ค.
์ฌ์ ์์ดํ ์ ๋ง์ ์ํ ๊ฐ(status)๊ณผ ๋ค์ํ ๋ทฐ๊ฐ ์์๊ธฐ์ ๋ค๋ฅธ ํ๋ก์ ํธ๋ณด๋ค ํ์ผ์ด ํจ์ฌ ๋ ๋ง์๋ค.
์ฌ์ง์ด ์์ ๋ง์ด ๊ฑฐ์ณ ๊ฐ ์ฅ๊ธฐ ํ๋ก์ ํธ์ด๊ณ , ์ ์ฌํ๊ธฐ ์ ๊น์ง ์ปจ๋ฒค์ ์ด ์์๊ธฐ์ ์ถฉ๊ฒฉ ๊ทธ ์์ฒด๋ค.๐คฌ
๊ฐ๋ฐ์๋ผ๋ฉด ์ด๋ฐ ์ํฉ์ ๋ณด๋ฉด ๋ฐ๋ก ๊ณ ์น๊ณ ์ถ์ง ์๊ฒ ๋๊ฐ๐ฅธ
์นํ ์๋๋ก์ด๋ ๊ฐ๋ฐ์๋ค์๊ฒ ์์๋ฌธํ์ง๋ง ๋ชจ๋ XML Shape๋ฅผ ์ฐ๊ณ ์์๋ค.๐
์ ๋งํ๋ ๊ธฐ์ ํ๋ก์ ํธ๋ ์ด๋ ๋ค๊ณ ํ๋ ๋๋๊ธด ํ๋ค.
๐ ๊ณ ๋ฏผํ ํด๊ฒฐ์ฑ ๋ค
1. MaterialButton ํ์ฉ
MaterialButton์๋ cornerRadius๋ผ๋ ์์ฑ์ด ์๋ค. inset, ripple ๋ฑ์ ์ ๊ฑฐํ๋ค๋ฉด ํ ์คํธ๋ทฐ๋ก ํ์ฉํ ์ ์์ง ์๊ฒ ๋๋๋ ์๊ฐ์ ํ๋ค.
Button์ด TextView ํ์ ๊ฐ์ฒด๋ผ๊ณ ํ์ง๋ง, ๋ถํ์ํ ๊ตฌํ์ด ํฌํจ๋์ด ์์ผ๋ ๊ฐ์ฒด ์งํฅ์ ์ธก๋ฉด์์๋ ๋ง์ง ์๋ค๊ณ ํ๋จํ๋ค.
2. ShapeDrawable
XML๋ก ์ ์๋ Shape๋ ๊ฒฐ๊ตญ์ ๋ ๋๋๊ธฐ ์ํด์๋ ์๋๋ก์ด๋ ์ฝ๋๋ก ๊ทธ๋ ค์ง ๊ฒ์ด๋ผ ์๊ฐํ๋ค.
ShapeDrawable์ด๋ผ๋ ์ด๋ฆ์ผ๋ก ๊ทธ๋ฅ ๊ฒ์ํด๋ดค๋๋ฐ ์กด์ฌํ๋ค.๐ฎ
๊ทธ๋ฆฌ๊ณ Radius์ Solid๊น์ง ์ฑ์ธ ์ ์์์ง๋ง, Stroke ์ค์ ์ด ๋ถ๊ฐ๋ฅํ๋ค.๐ข
3. GradientDrawable
์๋๋ก์ด๋ XML attributes๋ฅผ ์ดํด๋ณด๋ Shape XML์์ ์ฌ์ฉํ๋ corner/solid/stroke ๋ฑ GradientDrawable์ ์์ฑ๋ค๋ก ์ ์๋์ด ์์๋ค.
GradientDrawable์๋ Stroke๊น์ง ์ค์ ํ ์ ์์๋ค.๐
4. Compose๐
Compose์งฑ
๐ Code
GradientDrawable์ ์์ฑํ์ฌ View์ ๋ฐฑ๊ทธ๋ผ์ด๋์ ์ค์ ํด์ฃผ๋ ํ์์ ๊ณ ์ํ๋ค.
View ํ์ฅ ํจ์๋ฅผ ํตํด ์ฝ๋๋ก ์ธํ ํ ์ ์์ผ๋ฉฐ, DataBinding์ ํ์ฉํ์ฌ XML ๋ทฐ์์ ๋ฐ๋ก ์ ์ฉ๋ ๊ฐ๋ฅํ๋ค.
@BindingAdapter(
value = [
"setTopLeftCorners",
"setTopRightCorners",
"setBottomRightCorners",
"setBottomLeftCorners",
"setInsideBackgroundColor",
"setStrokeWidth",
"setStrokeColor"
], requireAll = false
)
fun View.setBackgroundShape(
@Px topLeftCorners: Float? = null,
@Px topRightCorners: Float? = null,
@Px bottomRightCorners: Float? = null,
@Px bottomLeftCorners: Float? = null,
@ColorInt insideBackgroundColor: Int? = null,
@Px strokeWidth: Float? = null,
@ColorInt strokeColor: Int? = null
) {
val corners = floatArrayOf(
topLeftCorners ?: 0f, topLeftCorners ?: 0f,
topRightCorners ?: 0f, topRightCorners ?: 0f,
bottomRightCorners ?: 0f, bottomRightCorners ?: 0f,
bottomLeftCorners ?: 0f, bottomLeftCorners ?: 0f
)
GradientDrawable().apply {
setStroke(strokeWidth?.toInt() ?: 0, strokeColor ?: android.R.color.transparent)
cornerRadii = corners
setColor(insideBackgroundColor ?: android.R.color.transparent)
}.run {
this@setBackgroundShape.background = this
}
}
๋๋ถ๋ถ์ ๋ฅ๊ทผ ํํ ๋ทฐ๋ ์ฝ๋๊ฐ์ด ์๋ก ๋ค๋ฅด์ง๋ ์๊ธฐ์ ์๋ ์ฝ๋๋ฅผ ํตํด ๋คํ์ฑ์ ์ฑ๊ฒผ๋ค.
@BindingAdapter(
value = [
"setTopCorners",
"setBottomCorners",
"setInsideBackgroundColor",
"setStrokeWidth",
"setStrokeColor"
], requireAll = false
)
fun View.setBackgroundShape(
@Px topCorners: Float? = null,
@Px bottomCorners: Float? = null,
@ColorInt insideBackgroundColor: Int? = null,
@Px strokeWidth: Float? = null,
@ColorInt strokeColor: Int? = null
) {
setBackgroundShape(
topLeftCorners = topCorners,
topRightCorners = topCorners,
bottomRightCorners = bottomCorners,
bottomLeftCorners = bottomCorners,
insideBackgroundColor = insideBackgroundColor,
strokeWidth = strokeWidth,
strokeColor = strokeColor
)
}
@BindingAdapter(
value = [
"setCorners",
"setInsideBackgroundColor",
"setStrokeWidth",
"setStrokeColor"
], requireAll = false
)
fun View.setBackgroundShape(
@Px corners: Float? = null,
@ColorInt insideBackgroundColor: Int? = null,
@Px strokeWidth: Float? = null,
@ColorInt strokeColor: Int? = null
) {
setBackgroundShape(
topLeftCorners = corners,
topRightCorners = corners,
bottomRightCorners = corners,
bottomLeftCorners = corners,
insideBackgroundColor = insideBackgroundColor,
strokeWidth = strokeWidth,
strokeColor = strokeColor
)
}
๋ทฐ ์ฝ๋๋ก ์์ฑํ๋ ๊ฒ์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๊ฒ ๋ค.
binding.txtHelloWorld.setBackgroundShape(
corners = 8.toPx,
insideBackgroundColor = ContextCompat.getColor(this, R.color.purple_500),
strokeWidth = 2.toPx,
strokeColor = ContextCompat.getColor(this, R.color.black)
)
// ์ถ๊ฐ ํ(?) : Px ๋จ์์ด๊ธฐ์ DpToPx ๋ณํ์ด ํ์ํ๋ค
val Int.toPx: Float
get() {
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, this.toFloat(), resources.displayMetrics)
}
์๋ ์คํฌ๋ฆฐ์ท์ ์๋จ ๋ฒํผ์ ๋ฐฉ๊ธ ์ ์ํ ํจ์์ ์ํด ๋ง๋ค์ด์ก์ผ๋ฉฐ, ํ๋จ ๋ฒํผ์ Shape XML๋ก ๋ง๋ค์ด์ก๋ค.
๋์ผํ Drawable ์ฆ๋ช
๋
๐ฎ ๋จ์ , ๊ทธ๋ฆฌ๊ณ Compose(?)
์ฝ๋ ๋๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ด๊ธฐ์ ํ๋ฆฌ๋ทฐ๋ฅผ ํ์ธํ ์ ์๋ค๋ ๊ฒ์ด ํฐ ๋จ์ ์ผ ๊ฒ ๊ฐ๋ค.
๋๋ ํ๋ฆฌ๋ทฐ๋ฅผ ํฐ ํ, ํํ๋ฅผ ์์๋ณด๊ธฐ ์ํด์๋ง ์ฌ์ฉํ๊ธด ํด์, ๋ ๊ฐ์ ๊ฒฝ์ฐ๋ ํฐ ๋ฌธ์ ๋ ์๋๋ค.
์ด๋ฅผ ํด๊ฒฐํ๊ธฐ ์ํด์ ์ปค์คํ ๋ทฐ๋ก TextView๋ฅผ ๋ง๋ค๊ฑฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํํ๋ก ์ ์ํ์ฌ ์ฌ๋ฌ ํ๋ก์ ํธ์์ ์ฌ์ฉํ ์ ์๊ฒ ์ง๋ง
๋ฌธ์ ๋ฅผ ๋๋ผ์ง ๋ชปํ์ฌ ์ฑํํ ๋ฐฉ๋ฒ์ ์๋๋ค.
TDS(Toss Design System)์ฒ๋ผ ์์ ๋์์ธ ํ์ด ์ผ๊ด์ ์ธ ํํ์ ๋ทฐ๋ฅผ ๊ณ ์ํ๋ค๋ฉด ์ปค์คํ ๋ทฐ๋ฅผ ๋ง๋ค์ด ํ์ฅ์ฑ์๊ฒ ์ ์ฉํด๋ณผ ์๊ฐ์ ์๋ค.
๊ฐ์ฅ ์ข์ ๋ฐฉ๋ฒ์ Compose๋ก ๋์ด๊ฐ๋ ๊ฒ์ด๋ค.
Modifier๋ก ์ฝ๊ฒ ์ฝ๋ ๊ฐ์ ์ค์ ํ ์ ์๊ณ ์ง๊ด์ ์ด๋ฉฐ ํ๋ฆฌ๋ทฐ๊น์ง ํด๊ฒฐ๋๋ค.
Button(
shape = RoundedCornerShape(23.dp),
border = BorderStroke(3.dp, Color.Red)
)
ํ์ง๋ง ๊ธฐ์กด ํ๋ก์ ํธ๋ฅผ Compose๋ก ์ ํํ๋ ๊ฒ์ ๋ฌด๋ฆฌ๊ฐ ์๋ค.
๋ฌธ์ (๋ฌธ์ ๊ฐ ์๋ ์๋ ์์ง๋ง)๋ฅผ ์ง๋ฉดํ๊ณ ์์ผ ๊ณ ๋ฏผ์ ํ๋ค๋ ๊ฒ ์ค์ค๋ก ์์ฌ์์ ๋น์ฐํ๊ฒ ๋์ด๊ฐ ๊ฒ๋ค์ ๋์๋ณด๊ณ ์๋ค.
๋ค์ํ ๋ฌธ์ ์ค ๋ทฐ์ ๊ด๋ จ๋ ๋ฌธ์ ๋๋ถ๋ถ์ Compose๋ก ํด๊ฒฐ์ด ๊ฐ๋ฅํ๊ธฐ์ ํ๋ก์ ํธ ๋จ์๋ก ๋นจ๋ฆฌ ๋ง๋ณด๊ณ ์ถ๋ค.
ํ์ฌ์์ ์ ์ฌ์ ์ ๋ ์ค๋นํ๊ณ ์๋๋ฐ ๊ทธ ํ๋ก์ ํธ๋ Compose๋ฅผ 100% ์ฌ์ฉํ ์์ ์ด๋ค.
(๋ฐ๋๋ผ์ธ ํ์ XML๋ก ๋์ด๊ฐ๋ ๊ฒ์ ์ ๋ ํ์ง๋ง์๊ณ , ๊ธ์งํ ๊ฒ์ด๋ผ๊ณ ํ์ ์ ์ธํด๋จ๋ค. ๋ด๊ฐ ๋ญ๋ผ๊ณ )
์ง๊ธ์ง๊ธํ XML, RecyclerView ํ์ถ!?๐
๋๊ธ