Compose の Brush
では、画面上で何かを描画する際の手法を記述します。これは、描画領域(円、四角、経路など)に適用される色を定義するものです。LinearGradient
ブラシ、RadialGradient
ブラシ、単色の SolidColor
ブラシなど、描画で役に立つ組み込みのブラシがいくつか用意されています。
ブラシは、Modifier.background()
、TextStyle
、DrawScope
の描画呼び出しで使用可能で、描画するコンテンツにペイント スタイルを適用できます。
たとえば、水平グラデーション ブラシを DrawScope
の円に適用できます。
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
グラデーション ブラシ
さまざまな組み込みのグラデーション ブラシを使用して、多様なグラデーション効果を適用できます。これらのブラシでは、グラデーションの生成で使用する色のリストを指定できます。
使用可能なグラデーション ブラシとそれぞれに対応する出力のリスト:
グラデーション ブラシのタイプ | 出力 |
---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
注: 色が滑らかに移り変わるように、最後の色を最初の色に設定します。 |
|
Brush.radialGradient(colorList) |
colorStops
で色の分布を変更する
グラデーションにあらわれる色をカスタマイズするには、それぞれの色の colorStops
値を調整します。colorStops
は 0 から 1 の割合で指定します。1 より大きい値を指定すると、その色はグラデーションの中でレンダリングされなくなります。
ある色の量を小さくしたり、あるいは大きくしたりなど、異なる量になるようにカラーストップを構成できます。
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)) )
色は、colorStop
ペアで定義される指定されたオフセットで分散され、赤と青よりも黄色が少なくなります。
TileMode
でパターンを繰り返す
各グラデーション ブラシには、TileMode
を設定するオプションがあります。グラデーションの開始と終了を設定していない場合、デフォルトで領域全体が塗りつぶされるため、TileMode
に気づかないかもしれません。TileMode
は、領域のサイズがブラシのサイズより大きい場合にのみ、グラデーションをタイル化します。
次のコードの場合、endX
が 50.dp
に設定され、サイズが 200.dp
に設定されているため、グラデーション パターンが 4 回繰り返されます。
val listColors = listOf(Color.Yellow, Color.Red, Color.Blue) val tileSize = with(LocalDensity.current) { 50.dp.toPx() } Box( modifier = Modifier .requiredSize(200.dp) .background( Brush.horizontalGradient( listColors, endX = tileSize, tileMode = TileMode.Repeated ) ) )
以下の表は、異なる TileMode が上記の HorizontalGradient
の例の場合でどのように機能するかを説明しています。
TileMode | 出力 |
---|---|
TileMode.Repeated : エッジで反転して、最後の色から最初の色の順で表示されます。 |
|
TileMode.Mirror : エッジで反転して、最後の色から最初の色の順で表示されます。 |
|
TileMode.Clamp : エッジで最後の色に固定されます。それ以降の領域は、その色に最も近い色で塗りつぶされます。 |
|
TileMode.Decal : 境界のサイズまでのみレンダリングされます。TileMode.Decal では、透明な黒を利用して元の境界の外側のコンテンツがサンプリングされますが、TileMode.Clamp ではエッジの色がサンプリングされます。 |
TileMode
は、他の方向性のあるグラデーションでも同様に機能します。違いは反復が発生する方向です。
ブラシのサイズを変更する
ブラシを適用する領域のサイズがわかっている場合は、上記の TileMode
セクションで説明したように、タイル endX
を設定できます。DrawScope
を開いている場合は、size
プロパティを使用して領域のサイズを取得できます。
描画する領域のサイズが不明な場合(たとえば、Brush
がテキストに適用される場合)、Shader
を拡張して、createShader
関数で描画領域のサイズを利用できます。
この例では、サイズを 4 分の 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) )
円形グラデーションなど、他のグラデーション ブラシのサイズを変更することもできます。サイズと中心を指定しない場合、グラデーションは DrawScope
の全領域を塗りつぶします。また、円形グラデーションの中心は DrawScope
領域の中心にデフォルトで設定されます。この場合、円形グラデーションの中心が、幅と高さのうち小さいほうの中心として表示されます。
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
半径サイズを最大に設定するように円形グラデーションが変更されると、次のように、円形グラデーションの効果が改善されることがわかります。
val largeRadialGradient = object : ShaderBrush() { override fun createShader(size: Size): Shader { val biggerDimension = maxOf(size.height, size.width) return RadialGradientShader( colors = listOf(Color(0xFF2be4dc), Color(0xFF243484)), center = size.center, radius = biggerDimension / 2f, colorStops = listOf(0f, 0.95f) ) } } Box( modifier = Modifier .fillMaxSize() .background(largeRadialGradient) )
シェーダーの作成に渡される実際のサイズは、呼び出された場所から決定されることに注意してください。デフォルトで、サイズが Brush
の最後の作成と異なる場合、または、シェーダーの作成に使用された状態オブジェクトが変更された場合、Brush
がその Shader
を内部で再割り当てします。
次のコードは、描画領域のサイズの変化に応じて、シェーダーを異なるサイズで 3 回作成します。
val colorStops = arrayOf( 0.0f to Color.Yellow, 0.2f to Color.Red, 1f to Color.Blue ) val brush = Brush.horizontalGradient(colorStops = colorStops) Box( modifier = Modifier .requiredSize(200.dp) .drawBehind { drawRect(brush = brush) // will allocate a shader to occupy the 200 x 200 dp drawing area inset(10f) { /* Will allocate a shader to occupy the 180 x 180 dp drawing area as the inset scope reduces the drawing area by 10 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) inset(5f) { /* will allocate a shader to occupy the 170 x 170 dp drawing area as the inset scope reduces the drawing area by 5 pixels on the left, top, right, bottom sides */ drawRect(brush = brush) } } } )
画像をブラシとして使用する
ImageBitmap を Brush
として使用するには、画像を ImageBitmap
として読み込み、ImageShader
ブラシを作成します。
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 DrawScope#drawCircle() Canvas(onDraw = { drawCircle(imageBrush) }, modifier = Modifier.size(200.dp))
ブラシが、背景、テキスト、キャンバスなど、異なる複数の描画に適用されます。出力は次のようになります。
ここでは、ImageBitmap
を使用してテキストもレンダリングされ、テキストのピクセルが描画されています。
高度な例: カスタムブラシ
AGSL RuntimeShader
ブラシ
AGSL では、GLSL Shader 機能が一部提供されています。Shader を AGSL で記述して、Compose のブラシで使用できます。
Shader ブラシを作成するには、まず AGSL シェーダー文字列として Shader を定義します。
@Language("AGSL") val CUSTOM_SHADER = """ uniform float2 resolution; layout(color) uniform half4 color; layout(color) uniform half4 color2; half4 main(in float2 fragCoord) { float2 uv = fragCoord/resolution.xy; float mixValue = distance(uv, vec2(0, 1)); return mix(color, color2, mixValue); } """.trimIndent()
上のシェーダーは 2 つの入力色を受け取り、描画領域の左下(vec2(0, 1)
)からの距離を計算して、距離に基づいて 2 つの色間で mix
を実行します。これにより、グラデーション効果が得られます。
次に、Shader ブラシを作成し、resolution
のユニフォームを設定します。これは、描画領域のサイズと、カスタム グラデーションへの入力として使用する color
と color2
です。
val Coral = Color(0xFFF3A397) val LightYellow = Color(0xFFF8EE94) @RequiresApi(Build.VERSION_CODES.TIRAMISU) @Composable @Preview fun ShaderBrushExample() { Box( modifier = Modifier .drawWithCache { val shader = RuntimeShader(CUSTOM_SHADER) val shaderBrush = ShaderBrush(shader) shader.setFloatUniform("resolution", size.width, size.height) onDrawBehind { shader.setColorUniform( "color", android.graphics.Color.valueOf( LightYellow.red, LightYellow.green, LightYellow .blue, LightYellow.alpha ) ) shader.setColorUniform( "color2", android.graphics.Color.valueOf( Coral.red, Coral.green, Coral.blue, Coral.alpha ) ) drawRect(shaderBrush) } } .fillMaxWidth() .height(200.dp) ) }
これを実行すると、画面に次のものがレンダリングされます。
なお、すべて数学ベースの計算であるため、シェーダーでは、単なるグラデーションだけでなく、はるかに多くのことを実現できます。AGSL の詳細については、AGSL のドキュメントをご覧ください。
参考情報
Compose でブラシを使用するその他の例については、以下のリソースをご確認ください。
- Compose でブラシテキストの色をアニメーションにする 🖌️
- Compose のカスタム グラフィックとレイアウト - Android Dev Summit 2022
- JetLagged サンプル - RuntimeShader ブラシ
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- グラフィック修飾子
- Compose のグラフィック
- テキストのスタイルを設定する