A Brush w Compose opisuje sposób rysowania czegoś na ekranie: określa kolory, które są rysowane w obszarze rysowania (np. koło, kwadrat, ścieżka). Dostępnych jest kilka wbudowanych pędzli, które są przydatne do rysowania,
np. LinearGradient, RadialGradient lub zwykły
SolidColor pędzel.
Pędzle można stosować za pomocą wywołań rysowania Modifier.background(), TextStyle lub
DrawScope, aby zastosować styl malowania do rysowanej treści.
Na przykład pędzel z gradientem poziomym można zastosować do rysowania koła w DrawScope:
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
Pędzle z gradientem
Dostępnych jest wiele wbudowanych pędzli z gradientem, których można używać do uzyskiwania różnych efektów gradientu. Te pędzle umożliwiają określenie listy kolorów, z których chcesz utworzyć gradient.
Lista dostępnych pędzli z gradientem i odpowiadających im wyników:
| Typ pędzla z gradientem | Wyniki |
|---|---|
Brush.horizontalGradient(colorList) |
|
Brush.linearGradient(colorList) |
|
Brush.verticalGradient(colorList) |
|
Brush.sweepGradient(colorList)
Uwaga: aby uzyskać płynne przejście między kolorami, ustaw ostatni kolor na kolor początkowy. |
|
Brush.radialGradient(colorList) |
|
Zmienianie rozkładu kolorów za pomocą colorStops
Aby dostosować sposób wyświetlania kolorów w gradiencie, możesz dostosować wartość colorStops dla każdego z nich. colorStops należy określić jako ułamek od 0 do 1. Wartości większe niż 1 spowodują, że te kolory nie będą renderowane jako część gradientu.
Możesz skonfigurować punkty zatrzymania kolorów tak, aby miały różne ilości, np. mniej lub więcej jednego koloru:
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)) )
Kolory są rozproszone z podanym przesunięciem zdefiniowanym w parze colorStop, czyli mniej żółtego niż czerwonego i niebieskiego.
Powtarzanie wzoru za pomocą TileMode
Każdy pędzel z gradientem ma opcję ustawienia TileMode. Jeśli nie ustawisz początku i końca gradientu, możesz nie zauważyć TileMode, ponieważ domyślnie wypełni on cały obszar. TileMode będzie powtarzać gradient tylko wtedy, gdy rozmiar obszaru jest większy niż rozmiar pędzla.
Poniższy kod powtórzy wzór gradientu 4 razy, ponieważ endX jest ustawiony na 50.dp, a rozmiar na 200.dp:
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 ) ) )
W tej tabeli znajdziesz szczegółowe informacje o tym, co robią poszczególne tryby powtarzania w przypadku powyższego przykładu HorizontalGradient:
| TileMode | Wyniki |
|---|---|
TileMode.Repeated: krawędź jest powtarzana od ostatniego koloru do pierwszego. |
|
TileMode.Mirror: krawędź jest odbijana od ostatniego koloru do pierwszego. |
|
TileMode.Clamp: krawędź jest przycinana do koloru końcowego. Następnie będzie malować najbliższy kolor w pozostałej części regionu. |
|
TileMode.Decal: renderuj tylko do rozmiaru granic. TileMode.Decal wykorzystuje przezroczystą czerń do próbkowania treści poza oryginalnymi granicami, a TileMode.Clamp próbkuje kolor krawędzi. |
|
TileMode działa podobnie w przypadku innych gradientów kierunkowych, z tą różnicą, że powtarzanie odbywa się w innym kierunku.
Zmienianie rozmiaru pędzla
Jeśli znasz rozmiar obszaru, w którym będzie rysowany pędzel, możesz ustawić endX kafelka, jak pokazano powyżej w sekcji TileMode. Jeśli jesteś w
a DrawScope, możesz użyć jego size właściwości, aby uzyskać rozmiar obszaru.
Jeśli nie znasz rozmiaru obszaru rysowania (np. jeśli
Brush jest przypisany do tekstu), możesz rozszerzyć Shader i wykorzystać rozmiar
obszaru rysowania w funkcji createShader.
W tym przykładzie podziel rozmiar przez 4, aby powtórzyć wzór 4 razy:
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) )
Możesz też zmienić rozmiar pędzla dowolnego innego gradientu, np. gradientu radialnego. Jeśli nie określisz rozmiaru i środka, gradient zajmie całe granice DrawScope, a środek gradientu promieniowego domyślnie będzie znajdować się w środku granic DrawScope. W rezultacie środek gradientu radialnego będzie znajdować się w środku mniejszego wymiaru (szerokości lub wysokości):
Box( modifier = Modifier .fillMaxSize() .background( Brush.radialGradient( listOf(Color(0xFF2be4dc), Color(0xFF243484)) ) ) )
Gdy zmienisz gradient promieniowy, aby ustawić rozmiar promienia na maksymalny wymiar, zobaczysz, że daje to lepszy efekt gradientu promieniowego:
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) )
Warto zauważyć, że rzeczywisty rozmiar przekazywany do tworzenia shadera jest określany na podstawie miejsca, w którym jest on wywoływany. Domyślnie Brush ponownie przydzieli swój Shader wewnętrznie, jeśli rozmiar różni się od ostatniego utworzenia Brush lub jeśli zmieni się obiekt stanu używany do tworzenia shadera.
Poniższy kod tworzy shader 3 razy z różnymi rozmiarami, ponieważ zmienia się rozmiar obszaru rysowania:
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) } } } )
Używanie obrazu jako pędzla
Aby użyć ImageBitmap jako Brush, wczytaj obraz jako ImageBitmap, i utwórz pędzel 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))
Pędzel jest stosowany do kilku różnych typów rysowania: tła, tekstu i Canvas. Wynik jest następujący:
Zwróć uwagę, że tekst jest teraz renderowany za pomocą ImageBitmap, aby malować piksele tekstu.
Zaawansowany przykład: pędzel niestandardowy
Pędzel AGSL RuntimeShader
AGSL oferuje podzbiór możliwości shadera GLSL. Shadery można pisać w AGSL i używać z pędzlem w Compose.
Aby utworzyć pędzel shader, najpierw zdefiniuj shader jako ciąg shadera AGSL:
@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()
Powyższy shader przyjmuje 2 kolory wejściowe, oblicza odległość od lewego dolnego rogu (vec2(0, 1)) obszaru rysowania i wykonuje mix między 2 kolorami na podstawie odległości. Daje to efekt gradientu.
Następnie utwórz pędzel shader i ustaw uniformy dla resolution – rozmiaru obszaru rysowania oraz color i color2, których chcesz użyć jako danych wejściowych do niestandardowego gradientu:
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) ) }
Po uruchomieniu tego kodu na ekranie zobaczysz:
Warto zauważyć, że za pomocą shaderów można robić znacznie więcej niż tylko gradienty, ponieważ są to obliczenia matematyczne. Więcej informacji o AGSL znajdziesz w dokumentacji AGSL.
Dodatkowe materiały
Więcej przykładów użycia pędzla w Compose znajdziesz w tych materiałach:
- Animowanie kolorowania tekstu pędzlem w Compose 🖌️
- Niestandardowa grafika i układy w Compose – Android Dev Summit 2022
- Przykład JetLagged – pędzel RuntimeShader
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy język JavaScript jest wyłączony.
- Modyfikatory grafiki
- Grafika w Compose
- Stylizowanie tekstu