Element Brush
w sekcji „Utwórz” określa, jak coś jest rysowane na ekranie: określa kolory rysowane w obszarze rysowania (np. koło, kwadrat, ścieżka). Do rysowania możesz używać kilku wbudowanych pędzli, takich jak LinearGradient
, RadialGradient
lub zwykły pędzel SolidColor
.
Pędzli można używać w połączeniu z metodami rysowania Modifier.background()
, TextStyle
lub DrawScope
, aby zastosować styl rysunku do rysowanej treści.
Na przykład pędzel z poziomym gradientem można zastosować do narysowania koła:DrawScope
val brush = Brush.horizontalGradient(listOf(Color.Red, Color.Blue)) Canvas( modifier = Modifier.size(200.dp), onDraw = { drawCircle(brush) } )
Pędzle gradientowe
Dostępnych jest wiele wbudowanych pędzli gradientów, które można wykorzystać do uzyskania różnych efektów gradientu. Te pędzle umożliwiają określenie listy kolorów, z których ma powstać gradient.
Lista dostępnych pędzli gradientu i ich odpowiednich wyników:
Typ pędzla gradientu | Urządzenie wyjściowe |
---|---|
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 jako kolor początkowy. |
|
Brush.radialGradient(colorList) |
Zmień rozkład kolorów za pomocą colorStops
Aby dostosować sposób wyświetlania kolorów w gradientach, możesz zmienić wartość parametru colorStops
dla każdego z nich. Wartość colorStops
powinna być podana jako ułamek w zakresie 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 koloru, aby miały różne wartości, na przykład 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 użyciem podanego przesunięcia, jak to określono w parze colorStop
, mniej żółte niż czerwone i niebieskie.
Powtarzanie wzoru za pomocą TileMode
Każdy gradientowy pędzel ma opcję TileMode
. Jeśli nie ustawisz początku ani końca gradientu, TileMode
może nie być widoczne, ponieważ domyślnie wypełnia ono całą powierzchnię. TileMode
będzie stosować 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
ma wartość 50.dp
, a rozmiar ma wartość 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 ) ) )
Oto tabela, która szczegółowo opisuje, jak różne tryby kafelków działają w przypadku przykładu HorizontalGradient
:
TileMode | Urządzenie wyjściowe |
---|---|
TileMode.Repeated : krawędź jest powtarzana od ostatniego koloru do pierwszego. |
|
TileMode.Mirror : krawędź jest lustrzanym odbiciem ostatniego koloru na pierwszy. |
|
TileMode.Clamp : krawędzie są przycinane do ostatecznego koloru. Następnie wypełnia resztę regionu najbliższym kolorem. |
|
TileMode.Decal : renderowanie tylko do rozmiaru granic. TileMode.Decal wykorzystuje przezroczystą czerń do próbkowania treści poza oryginalnymi granicami, podczas gdy TileMode.Clamp próbkuje kolor krawędzi. |
Funkcja TileMode
działa w podobny sposób w przypadku innych gradientów kierunkowych, z tą różnicą, że powtórzenie występuje w innym kierunku.
Zmiana rozmiaru pędzla
Jeśli znasz rozmiar obszaru, na którym ma działać pędzel, możesz ustawić kafelek endX
tak, jak to opisaliśmy powyżej w sekcji TileMode
. Jeśli znajdujesz się w DrawScope
, możesz użyć właściwości size
, aby uzyskać rozmiar obszaru.
Jeśli nie znasz rozmiaru obszaru rysunku (np. jeśli Brush
jest przypisany do tekstu), możesz rozszerzyć Shader
i wykorzystać rozmiar obszaru rysunku 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 promieniowego. Jeśli nie określisz rozmiaru i środka, gradient zajmie pełne granice elementu DrawScope
, a środek gradientu promienistego będzie domyślnie ustawiony na środek granic DrawScope
. W efekcie środek gradientu promienistego 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 gradient promieniowy zostanie zmieniony tak, aby rozmiar promienia odpowiadał maksymalnej wymiarowi, zobaczysz, że daje on 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 pamiętać, że rzeczywisty rozmiar przekazywany do tworzenia shadera jest określany na podstawie miejsca, w którym jest wywoływany. Domyślnie Brush
przydzieli wewnętrznie Shader
, jeśli rozmiar jest inny niż podczas ostatniego utworzenia Brush
lub jeśli obiekt stanu użyty do utworzenia shadera uległ zmianie.
Podany niżej kod tworzy shader 3 razy w różnych rozmiarach, gdy zmienia się obszar 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
, załaduj 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 rysunków: tła, tekstu i płótna. Wynik:
Zwróć uwagę, że tekst jest teraz renderowany za pomocą ImageBitmap
, aby wypełnić piksele tekstu.
Przykład zaawansowany: pędzel niestandardowy
Pędzel AGSL RuntimeShader
AGSL oferuje podzbiór funkcji GLSL Shader. Programiści mogą pisać shadery w języku AGSL i używać ich z pędzlem w Compose.
Aby utworzyć pędzel Shader, najpierw zdefiniuj shader jako ciąg znaków 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()
Podany powyżej shader przyjmuje 2 kolory wejściowe, oblicza odległość od dolnej lewej krawędzi (vec2(0, 1)
) obszaru rysunku i wykonywuje mix
między tymi 2 kolorami na podstawie odległości. Powoduje to efekt gradientu.
Następnie utwórz pędzel Shader i ustaw wartości uniformów dla resolution
– rozmiaru obszaru rysunku oraz color
i color2
, które chcesz użyć jako dane wejściowe do gradientu niestandardowego:
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 zobaczysz na ekranie następujące elementy:
Warto pamiętać, że shadery mogą służyć do innych celów niż tylko do tworzenia gradientów, ponieważ są to obliczenia oparte na matematyce. Więcej informacji o AGSL znajdziesz w dokumentacji dotyczącej tego tematu.
Dodatkowe materiały
Więcej przykładów korzystania z narzędzia Pędzel w usłudze Compose znajdziesz w tych materiałach:
- Animowanie kolorowania tekstu za pomocą pędzla w sekcji Tworzenie 🖌️
- Niestandardowe grafiki i schematy w Compose – Android Dev Summit 2022
- JetLagged Sample – RuntimeShader Brush
Polecane dla Ciebie
- Uwaga: tekst linku jest wyświetlany, gdy obsługa JavaScript jest wyłączona
- Modyfikatory graficzne
- Grafika w sekcji Nowy post
- Tekst stylizowany