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 w 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 chcesz utworzyć 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 zgodnie z podanym przesunięciem zdefiniowanym 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, możesz nie zauważyć opcji TileMode
, ponieważ domyślnie wypełni ona cały obszar. 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 powyższego 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ć płytkę endX
tak, jak to opisaliśmy 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 promieniowego 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. Shadery można pisać 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 uniformy 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ż wszystkie obliczenia są 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 układy 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