באפליקציות רבות צריך לשלוט במדויק במה שמצויר במסך. יכול להיות שמדובר בפעולה פשוטה כמו הצבת ריבוע או עיגול במסך במקום הנכון, או בסידור מורכב של רכיבים גרפיים בסגנונות שונים.
ציור בסיסי באמצעות מקשי שינוי ו-DrawScope
הדרך העיקרית לצייר משהו בהתאמה אישית ב-Compose היא באמצעות מודификаторים, כמו Modifier.drawWithContent
, Modifier.drawBehind
ו-Modifier.drawWithCache
.
לדוגמה, כדי לצייר משהו מאחורי ה-Composable, אפשר להשתמש במקש המשנה drawBehind
כדי להתחיל לבצע פקודות ציור:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
אם כל מה שאתם צריכים הוא רכיב מורכב שאפשר לצייר בו, תוכלו להשתמש ברכיב המורכב Canvas
. ה-composable של Canvas
הוא מעטפת נוחה של Modifier.drawBehind
. אפשר להוסיף את Canvas
לפריסה באותו אופן שבו מוסיפים כל רכיב אחר בממשק המשתמש של Compose. ב-Canvas
אפשר לצייר אלמנטים עם שליטה מדויקת על הסגנון והמיקום שלהם.
כל המשתנים של הציור חושפים DrawScope
, סביבה של ציור ברמת ההיקף שמנהלת את המצב שלה. כך אפשר להגדיר את הפרמטרים של קבוצה של אלמנטים גרפיים. DrawScope
מספק כמה שדות שימושיים, כמו size
, שהוא אובייקט Size
שמציין את המימדים הנוכחיים של DrawScope
.
כדי לצייר משהו, אפשר להשתמש באחת מפונקציות הציור הרבות ב-DrawScope
. לדוגמה, הקוד הבא מצייר מלבן בפינה הימנית העליונה של המסך:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F drawRect( color = Color.Magenta, size = canvasQuadrantSize ) }
מידע נוסף על גורמים שונים לשינוי צורת הציור זמין במסמכי העזרה בנושא Graphics Modifiers.
מערכת קואורדינטות
כדי לצייר משהו במסך, צריך לדעת את הזזת האופרטור (x
ו-y
) ואת הגודל של הפריט. בחלק גדול משיטות הציור ב-DrawScope
, המיקום והגודל מסופקים על ידי ערכי ברירת המחדל של הפרמטרים. בדרך כלל, הפרמטרים שמוגדרים כברירת מחדל ממיקמים את הפריט בנקודה [0, 0]
בבד הציור, ומספקים ערך size
שממלא את כל אזור הציור, כמו בדוגמה שלמעלה – אפשר לראות שהריבוע ממוקם בפינה הימנית העליונה. כדי לשנות את הגודל והמיקום של הפריט, צריך להבין את מערכת הקואורדינטות ב-Compose.
המקור של מערכת הקואורדינטות ([0,0]
) נמצא בפיקסל הימני העליון ביותר באזור הציור. הערך של x
עולה ככל שהוא נע ימינה, והערך של y
עולה ככל שהוא נע למטה.
לדוגמה, אם רוצים לצייר קו אלכסוני מהפינה השמאלית העליונה של אזור הלוח ועד לפינה השמאלית התחתונה, אפשר להשתמש בפונקציה DrawScope.drawLine()
ולציין את הזזת ההתחלה והסיום עם המיקומים התואמים של x ו-y:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasWidth = size.width val canvasHeight = size.height drawLine( start = Offset(x = canvasWidth, y = 0f), end = Offset(x = 0f, y = canvasHeight), color = Color.Blue ) }
טרנספורמציות בסיסיות
DrawScope
מציע טרנספורמציות לשינוי המיקום או האופן שבו פקודות הציור מתבצעות.
שינוי גודל
אפשר להשתמש ב-DrawScope.scale()
כדי להגדיל את הגודל של פעולות השרטוט לפי גורם. פעולות כמו scale()
חלות על כל פעולות הציור בתוך פונקציית הלמהדה המתאימה. לדוגמה, הקוד הבא מגדיל את הערך של scaleX
פי 10 ואת הערך של scaleY
פי 15:
Canvas(modifier = Modifier.fillMaxSize()) { scale(scaleX = 10f, scaleY = 15f) { drawCircle(Color.Blue, radius = 20.dp.toPx()) } }
תרגום
משתמשים בDrawScope.translate()
כדי להזיז את פעולות הציור למעלה, למטה, ימינה או שמאלה. לדוגמה, הקוד הבא מעביר את הציור 100 פיקסלים ימינה ו-300 פיקסלים למעלה:
Canvas(modifier = Modifier.fillMaxSize()) { translate(left = 100f, top = -300f) { drawCircle(Color.Blue, radius = 200.dp.toPx()) } }
סיבוב
משתמשים ב-DrawScope.rotate()
כדי לסובב את פעולות השרטוט סביב נקודת ציר. לדוגמה, הקוד הבא מסובב מלבן ב-45 מעלות:
Canvas(modifier = Modifier.fillMaxSize()) { rotate(degrees = 45F) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }
מוטמע
משתמשים ב-DrawScope.inset()
כדי לשנות את הפרמטרים שמוגדרים כברירת מחדל של DrawScope
הנוכחי, לשנות את גבולות הציור ולתרגם את הציורים בהתאם:
Canvas(modifier = Modifier.fillMaxSize()) { val canvasQuadrantSize = size / 2F inset(horizontal = 50f, vertical = 30f) { drawRect(color = Color.Green, size = canvasQuadrantSize) } }
הקוד הזה מוסיף למעשה מרווח לפקודות השרטוט:
טרנספורמציות מרובות
כדי להחיל מספר טרנספורמציות על הציורים, משתמשים בפונקציה DrawScope.withTransform()
, שמאפשרת ליצור ולהחיל טרנספורמציה אחת שמשלבת את כל השינויים הרצויים. השימוש ב-withTransform()
יעיל יותר מביצוע קריאות בתצוגת עץ לטרנספורמציות נפרדות, כי כל הטרנספורמציות מתבצעות יחד בפעולה אחת, במקום ש-Compose יצטרך לחשב ולשמור כל אחת מהטרנספורמציות בתצוגת העץ.
לדוגמה, הקוד הבא מחיל על המלבן גם תרגום וגם סיבוב:
Canvas(modifier = Modifier.fillMaxSize()) { withTransform({ translate(left = size.width / 5F) rotate(degrees = 45F) }) { drawRect( color = Color.Gray, topLeft = Offset(x = size.width / 3F, y = size.height / 3F), size = size / 3F ) } }
פעולות נפוצות של ציור
ציור טקסט
כדי לצייר טקסט ב-Compose, בדרך כלל אפשר להשתמש ב-composable Text
. עם זאת, אם אתם נמצאים ב-DrawScope
או שאתם רוצים לצייר את הטקסט באופן ידני עם התאמה אישית, תוכלו להשתמש ב-method DrawScope.drawText()
.
כדי לצייר טקסט, יוצרים TextMeasurer
באמצעות rememberTextMeasurer
ומפעילים את drawText
עם המכשיר למדידת המרחק:
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }
מדידת טקסט
ציור טקסט פועל בצורה קצת שונה מפקודות ציור אחרות. בדרך כלל, מציינים בפקודת הציור את הגודל (רוחב וגובה) שבו רוצים לצייר את הצורה או התמונה. בטקסט, יש כמה פרמטרים ששולטים בגודל הטקסט שעבר רינדור, כמו גודל הגופן, הגופן, האותיות המקושרות ומרווחי האותיות.
ב-Compose, אפשר להשתמש ב-TextMeasurer
כדי לקבל גישה לגודל שנמדד של הטקסט, בהתאם לגורמים שמפורטים למעלה. אם רוצים לצייר רקע מאחורי הטקסט, אפשר להשתמש במידע שנמדד כדי לקבל את גודל האזור שבו הטקסט תופס:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixedWidth((size.width * 2f / 3f).toInt()), style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
קטע הקוד הזה יוצר רקע ורוד לטקסט:
שינוי האילוצים, גודל הגופן או כל מאפיין שמשפיע על הגודל שנמדד גורם לדיווח על גודל חדש. אפשר להגדיר גודל קבוע גם ל-width
וגם ל-height
, ואז הטקסט יתאים לגודל של TextOverflow
. לדוגמה, הקוד הבא יוצר טקסט ב-⅓ מהגובה וב-⅓ מהרוחב של אזור ה-Composable, ומגדיר את TextOverflow
כ-TextOverflow.Ellipsis
:
val textMeasurer = rememberTextMeasurer() Spacer( modifier = Modifier .drawWithCache { val measuredText = textMeasurer.measure( AnnotatedString(longTextSample), constraints = Constraints.fixed( width = (size.width / 3f).toInt(), height = (size.height / 3f).toInt() ), overflow = TextOverflow.Ellipsis, style = TextStyle(fontSize = 18.sp) ) onDrawBehind { drawRect(pinkColor, size = measuredText.size.toSize()) drawText(measuredText) } } .fillMaxSize() )
הטקסט מצויר עכשיו במגבלות עם שלוש נקודות בסוף:
ציור תמונה
כדי לצייר ImageBitmap
באמצעות DrawScope
, צריך לטעון את התמונה באמצעות ImageBitmap.imageResource()
ואז לבצע קריאה ל-drawImage
:
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })
ציור צורות בסיסיות
יש הרבה פונקציות לציור צורות ב-DrawScope
. כדי לצייר צורה, משתמשים באחת מפונקציות השרטוט שהוגדרו מראש, כמו drawCircle
:
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API |
פלט |
ציור נתיב
נתיב הוא סדרה של הוראות מתמטיות שמניבות ציור לאחר ביצוען. DrawScope
יכול לצייר נתיב באמצעות השיטה DrawScope.drawPath()
.
לדוגמה, נניח שרוצים לצייר משולש. אפשר ליצור נתיב באמצעות פונקציות כמו lineTo()
ו-moveTo()
לפי גודל אזור הציור.
לאחר מכן, קוראים ל-drawPath()
עם הנתיב החדש שנוצר כדי ליצור משולש.
Spacer( modifier = Modifier .drawWithCache { val path = Path() path.moveTo(0f, 0f) path.lineTo(size.width / 2f, size.height / 2f) path.lineTo(size.width, 0f) path.close() onDrawBehind { drawPath(path, Color.Magenta, style = Stroke(width = 10f)) } } .fillMaxSize() )
גישה לאובייקט Canvas
ב-DrawScope
אין לכם גישה ישירה לאובייקט Canvas
. אפשר להשתמש ב-DrawScope.drawIntoCanvas()
כדי לקבל גישה לאובייקט Canvas
עצמו, שבו אפשר להפעיל פונקציות.
לדוגמה, אם יש לכם Drawable
בהתאמה אישית שאתם רוצים לצייר על הלוח, תוכלו לגשת ללוח ולקרוא לפונקציה Drawable#draw()
, ולהעביר את האובייקט Canvas
:
val drawable = ShapeDrawable(OvalShape()) Spacer( modifier = Modifier .drawWithContent { drawIntoCanvas { canvas -> drawable.setBounds(0, 0, size.width.toInt(), size.height.toInt()) drawable.draw(canvas.nativeCanvas) } } .fillMaxSize() )
מידע נוסף
למידע נוסף על ציור ב-Compose, תוכלו לעיין במקורות המידע הבאים:
- Graphics Modifiers – מידע על הסוגים השונים של מודפי ציור.
- מברשת – כאן מוסבר איך להתאים אישית את ציור התוכן.
- פריסות וגרפיקה בהתאמה אישית ב-Compose – Android Dev Summit 2022 – איך יוצרים ממשק משתמש בהתאמה אישית ב-Compose באמצעות פריסות וגרפיקה.
- JetLagged Sample – דוגמה ל-Compose שמראה איך לצייר תרשים מותאם אישית.
מומלץ עבורך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- מפעילי גרפיקה
- גרפיקה ב-Compose
- קווים של יישור ב-Jetpack פיתוח נייטיב