Primary/Compose

Brush로 다양한 색깔 효과 입히기

해스끼 2023. 1. 4. 15:04

Compose에서 ``Brush``는 화면에 그려지는 원, 사각형, path 등이 어떻게 그려질지를 나타낸다. 단색을 표현하는 ``SolidColor``도 있지만, 주로 그라디언트를 표현하기 위해 사용한다. 

 

Brush는 ``Modifier.background()``, ``TextStyle``, ``DrawScope``에서 사용할 수 있다. 텍스트에도 brush를 적용할 수 있다는 점 기억하기!

그라디언트

Brush에 정의되어 있는 그라디언트는 다음과 같다. 모두 ``Brush.[some]Gradient(color)`` 형태로 사용할 수 있다.

horizontalGradient
linearGradient
verticalGradient
sweepGradient
radialGradient

이름만 보면 쉽게 이해할 수 있다. Horizontal과 vertical은 수평 및 수직 그라디언트를 나타내고, linear는 일반적인 직선 그라디언트를 나타내며, sweep과 radial은 서로 다른 형태의 원형 그라디언트를 나타낸다.

색 분포를 바꾸고 싶다면?

위에 정의된 함수들은 기본적으로 색을 고르게 사용한다. 특정 색의 빈도를 바꾸고 싶다면 ``colorStops`` 매개변수를 주면 된다. 0부터 1 사이의 실수 값을 사용하여 색의 비중을 나타낼 수 있는데, 0과 1은 각각 그라디언트의 시작과 끝을 의미한다. 따라서 0 이하 또는 1 이상의 색은 그라디언트에 나타나지 않는다.

 

예시는 다음과 같다. 노랑~빨강 비율이 20%, 빨강~파랑 비율이 80%인 그라디언트를 만들었다.

val colorStops = arrayOf(
    0.0f to Color.Yellow,
    0.2f to Color.Red,
    1f to Color.Blue
)
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(Brush.horizontalGradient(colorStops = colorStops))
)

ok

패턴 반복하기

Brush에 정의된 그라디언트 함수를 사용할 때 ``TileMode`` 매개변수를 지정할 수 있다. 그라디언트는 기본적으로 영역 전체를 채우지만, 그라디언트가 영역보다 작다면 ``TileMode`` 값을 사용하여 패턴이 어떻게 반복될 지 정할 수 있다.

 

200dp짜리 화면에 50dp짜리 그라디언트를 채우는 예시를 살펴보자.

 

먼저 ``TileMode.Repeated``이다. 이름에서 알 수 있듯이 같은 패턴을 반복한다.

TileMode.Repeated

다음으로 ``TileMode.Mirror``이다. 말 그대로 색이 거울처럼 반복된다. 123 321 123 321과 같은 패턴이다.

TileMode.Repeated

``TileMode.Clamp``는 남은 부분을 마지막 색으로 채운다.

마지막으로 ``TileMode.Decal``은 그라디언트를 반복하지 않는다. 정확히는 남은 부분을 투명하게 채운다. 컨텐츠를 살짝만 보여주고 싶을 때 사용할 수 있을 듯.

``TileMode.Decal``은 API 31(Android 12) 이상에서만 사용할 수 있다. ``TileMode.Decal.isSupported()``를 호출하면 값을 사용할 수 있는지 확인할 수 있다. API 31 미만에서는 ``TileMode.Clamp``가 대신 사용된다.

Brush 크기 지정

``Canvas`` 등의 ``DrawScope``에서는 ``size`` 값에 접근하여 화면의 크기를 얻을 수 있다. 이 값을 바탕으로 그라디언트의 크기를 정할 수 있다.

 

``DrawScope`` 밖에서 그라디언트의 크기를 정하고 싶다면, ``ShaderBrush``를 상속받아 ``createShader`` 함수를 정의하자. ``createShader`` 함수에 전달되는 ``size`` 매개변수에 접근하면 된다.

 

``ShaderBrush``를 통해 화면의 1/4를 채우는 그라디언트를 정의한 예시이다.

val listColors = listOf(Color.Yellow, Color.Red, Color.Blue)
val customBrush = remember {
    object : ShaderBrush() {
        override fun createShader(size: Size): Shader {
            return LinearGradientShader(
                colors = listColors,
                from = Offset.Zero,
                to = Offset(size.width / 4f, 0f),
                tileMode = TileMode.Mirror
            )
        }
    }
}
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(customBrush)
)

원형 그라디언트의 크기도 바꿀 수 있다. 원형 그라디언트의 크기는 기본적으로 너비와 높이 중 작은 값으로 결정된다.

다음과 같이 너비와 높이 중 큰 값으로 설정하면 좀 더 나아 보인다.

Brush 내부에 정의된 Shader의 크기는 해당 brush가 호출되는 위치에서 결정된다. 원문에서는 다음과 같이 서술하고 있다.

By default, Brush will reallocate its Shader internally if the size is different from the last creation of the Brush, or if a state object used in creation of the shader has changed.

즉 brush가 호출된 영역의 크기가 직전에 호출된 영역과 다를 때, brush 내부의 shader가 새로 할당된다.

Image Brush

``ImageBitmap``을 brush로 사용할 수 있다.

val imageBrush =
    ShaderBrush(ImageShader(ImageBitmap.imageResource(id = R.drawable.dog)))

// Use ImageShader Brush with background
Box(
    modifier = Modifier
        .requiredSize(200.dp)
        .background(imageBrush)
)

// Use ImageShader Brush with TextStyle
Text(
    text = "Hello Android!",
    style = TextStyle(
        brush = imageBrush,
        fontWeight = FontWeight.ExtraBold,
        fontSize = 36.sp
    )
)

// Use ImageShader Brush with TextStyle
Text(
    text = "Hello Android!",
    style = TextStyle(
        brush = imageBrush,
        fontWeight = FontWeight.ExtraBold,
        fontSize = 36.sp
    )
)

// Use ImageShader Brush with DrawScope#drawCircle()
Canvas(onDraw = {
    drawCircle(imageBrush)
}, modifier = Modifier.size(200.dp))

텍스트에도 이미지가 잘 적용된다. 귀엽네~

참고문헌

 

브러시: 그라데이션 및 셰이더  |  Jetpack Compose  |  Android Developers

브러시: 그라데이션 및 셰이더 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Compose의 Brush는 화면에 무언가가 그려지는 방식을 설명합니다. 그리기 영역(원

developer.android.com