Brush
ב-Compose מתאר איך משהו מצויר על המסך: הוא קובע את הצבעים שמצוירים באזור הציור (כלומר, מעגל, ריבוע, נתיב). יש כמה מברשות מובנות ששימושיות לציור, כמו 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
תהיה רק חלק מהשיפוע אם גודל האזור גדול יותר מגודל המברשת.
הקוד הבא יחזור על דוגמת הגרדיאנט 4 פעמים, כי הערך של endX
מוגדר ל-50.dp
והגודל מוגדר ל-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 ) ) )
בטבלה הבאה מפורטות הפעולות של מצבי התצוגה השונים של המשבצות בדוגמה HorizontalGradient
שלמעלה:
TileMode | פלט |
---|---|
TileMode.Repeated : צבע הקצה חוזר על עצמו מהצבע האחרון לראשון. |
![]() |
TileMode.Mirror : הצבעים של הקצה משתקפים מהצבע האחרון לראשון. |
![]() |
TileMode.Clamp : קצה המעבר מוגבל לצבע הסופי. לאחר מכן, המערכת תצבע את שאר האזור בצבע הכי קרוב. |
![]() |
TileMode.Decal : הצגה רק עד לגודל הגבולות. TileMode.Decal משתמש בשחור שקוף כדי לדגום תוכן מחוץ לגבולות המקוריים, ואילו TileMode.Clamp דוגם את צבע הקצה. |
![]() |
TileMode
פועל באופן דומה עבור שאר הגרדיאנטים הכיווניים, ההבדל הוא הכיוון שבו מתרחש החזרה.
שינוי גודל המברשת
אם אתם יודעים את הגודל של האזור שבו המברשת תצייר, אתם יכולים להגדיר את המשבצת endX
כמו שראינו למעלה בקטע TileMode
. אם אתם נמצאים בDrawScope
, אתם יכולים להשתמש במאפיין size
כדי לקבל את גודל האזור.
אם אתם לא יודעים מה הגודל של אזור הציור (לדוגמה, אם Brush
מוקצה לטקסט), אתם יכולים להרחיב את Shader
ולהשתמש בגודל של אזור הציור בפונקציה createShader
.
בדוגמה הזו, מחלקים את הגודל ב-4 כדי לחזור על התבנית 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) )

חשוב לציין שהגודל בפועל שמועבר ליצירה של shader נקבע לפי המקום שבו הוא מופעל. כברירת מחדל, הפונקציה Brush
תקצה מחדש את Shader
באופן פנימי אם הגודל שונה מהגודל של היצירה האחרונה של Brush
, או אם אובייקט מצב שנעשה בו שימוש ביצירת הצללית השתנה.
הקוד הבא יוצר את ה-shader שלוש פעמים שונות עם גדלים שונים, כשהגודל של אזור הציור משתנה:
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 מציע קבוצת משנה של יכולות Shader של GLSL. אפשר לכתוב שיידרים ב-AGSL ולהשתמש בהם עם מברשת בכתיבה מהירה.
כדי ליצור מברשת Shader, קודם מגדירים את ה-Shader כמחרוזת Shader של 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()
ה-shader שלמעלה מקבל שני צבעי קלט, מחשב את המרחק מהפינה השמאלית התחתונה (vec2(0, 1)
) של אזור הציור ומבצע mix
בין שני הצבעים על סמך המרחק. כך נוצר אפקט של מעבר הדרגתי.
לאחר מכן, יוצרים את Shader Brush ומגדירים את המשתנים האחידים עבור 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) ) }
אחרי שמריצים את הפקודה, רואים את הפלט הבא במסך:

חשוב לציין שאפשר לעשות הרבה יותר עם shaders מאשר רק מעברי צבע, כי הכול מבוסס על חישובים מתמטיים. מידע נוסף על AGSL זמין במסמכי התיעוד.
מקורות מידע נוספים
דוגמאות נוספות לשימוש במברשת בכתיבה זמינות במקורות המידע הבאים:
- הוספת אנימציה לצביעת טקסט במברשת בכתיבה 🖌️
- Custom Graphics and Layouts in Compose - Android Dev Summit 2022
- JetLagged Sample - RuntimeShader Brush
מומלץ עבורך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- משני גרפיקה
- גרפיקה ב-Compose
- עיצוב טקסט