לאפליקציות רבות צריכה להיות יכולת לשלוט במדויק בתוכן שמשורטט על המסך. יכול להיות שמדובר בפעולה פשוטה כמו הצבת ריבוע או עיגול במסך במקום הנכון, או בסידור מורכב של רכיבים גרפיים בסגנונות שונים.
ציור בסיסי באמצעות מקשי שינוי ו-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 ) }
במסמכי העזרה של מגבילי גרפיקה תוכלו לקרוא מידע נוסף על מגבילי שרטוט שונים.
מערכת קואורדינטות
כדי לצייר משהו במסך, צריך לדעת את הזזת האופרטור (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 ) } }
פעולות שרטוט נפוצות
ציור טקסט
כדי לצייר טקסט בהצעות לכתיבה, בדרך כלל אפשר להשתמש בתוכן הקומפוזבילי Text
. עם זאת, אם משתמשים ב-DrawScope
או שרוצים לשרטט את הטקסט באופן ידני באמצעות התאמה אישית, אפשר להשתמש בשיטה DrawScope.drawText()
.
כדי לצייר טקסט, יוצרים TextMeasurer
באמצעות rememberTextMeasurer
ומפעילים את drawText
עם המכשיר למדידת המרחק:
val textMeasurer = rememberTextMeasurer() Canvas(modifier = Modifier.fillMaxSize()) { drawText(textMeasurer, "Hello") }
מדידת טקסט
ציור טקסט פועל בצורה קצת שונה מפקודות ציור אחרות. בדרך כלל, מציינים בפקודת הציור את הגודל (רוחב וגובה) שבו רוצים לצייר את הצורה או התמונה. בטקסט, יש כמה פרמטרים ששולטים בגודל הטקסט שעבר רינדור, כמו גודל הגופן, הגופן, האותיות המקושרות ומרווחי האותיות.
כשאתם מתחילים לכתוב, תוכלו להשתמש ב-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
. לדוגמה, הקוד הבא מעבד את הטקסט ב-1⁄3 מהגובה וב-1⁄3 מהרוחב של השטח הקומפוזבילי, ומגדיר את 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 מושבת
- מפעילי גרפיקה
- גרפיקה בכתיבה
- קווי יישור ב-Jetpack פיתוח נייטיב