הערך 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
מוקצה ל-Text), אפשר להרחיב את 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 השתנה.
הקוד הבא יוצר את ה-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
כדי לצייר את הפיקסלים של הטקסט.
דוגמה מתקדמת: מברשת בהתאמה אישית
מברשת RuntimeShader
של AGSL
AGSL מציע קבוצת משנה של יכולות Shader של GLSL. אפשר לכתוב Shaders ב-AGSL ולהשתמש בהם עם מברשת ב-Compose.
כדי ליצור מברשת 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 ומגדירים את המשתנים הקבועים של 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 – Android Dev Summit 2022
- דוגמה ל-JetLagged – מברשת RuntimeShader
מומלץ עבורך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- מפעילי גרפיקה
- גרפיקה ב-Compose
- עיצוב טקסט