אפליקציות רבות צריכות להיות מסוגלות לשלוט בדיוק במה שמוצג במסך. יכול להיות שמדובר בתיבה או בעיגול שמוצבים על המסך במקום הנכון, או בסידור מורכב של אלמנטים גרפיים בסגנונות שונים.
שרטוט בסיסי עם משנים ו-DrawScope
הדרך העיקרית לצייר משהו בהתאמה אישית ב-Compose היא באמצעות משנים (modifiers), כמו Modifier.drawWithContent, Modifier.drawBehind ו-Modifier.drawWithCache.
לדוגמה, כדי לצייר משהו מאחורי רכיב ה-Composable, אפשר להשתמש במאפיין drawBehind כדי להתחיל להריץ פקודות ציור:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
אם אתם צריכים רק קומפוזבל לציור, אתם יכולים להשתמש בקומפוזבל Canvas. הקומפוזיציה 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() חלות על כל פעולות הציור בתוך פונקציית ה-lambda המתאימה. לדוגמה, הקוד הבא מגדיל את 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 ) } }
rotate() כדי להחיל סיבוב על היקף הציור הנוכחי, שמסובב את המלבן ב-45 מעלות.
מוטמע
משתמשים ב-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() יעיל יותר מאשר ביצוע קריאות מקוננות לטרנספורמציות נפרדות, כי כל הטרנספורמציות מתבצעות יחד בפעולה אחת, במקום שפיתוח נייטיב יצטרך לחשב ולשמור כל אחת מהטרנספורמציות המקוננות.
לדוגמה, הקוד הבא מחיל גם תרגום וגם סיבוב על המלבן:
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 ) } }
withTransform כדי להחיל גם סיבוב וגם הזזה, לסובב את המלבן ולהזיז אותו שמאלה.פעולות נפוצות שקשורות לציור
ציור טקסט
כדי לצייר טקסט ב-Compose, בדרך כלל משתמשים בקוד Text קומפוזבילי. עם זאת, אם אתם נמצאים בDrawScope או שאתם רוצים לצייר את הטקסט באופן ידני עם התאמה אישית, אתם יכולים להשתמש בשיטה 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 שהוגדר. לדוגמה, הקוד הבא מציג טקסט בשליש מהגובה ובשליש מהרוחב של אזור הקומפוזבילי, ומגדיר את 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() )
הטקסט מוצג עכשיו באילוצים עם שלוש נקודות בסוף:
TextOverflow.Ellipsis עם אילוצים קבועים למדידת טקסט.ציור תמונה
כדי לצייר ImageBitmap באמצעות DrawScope, טוענים את התמונה באמצעות
ImageBitmap.imageResource() ואז קוראים ל-drawImage:
val dogImage = ImageBitmap.imageResource(id = R.drawable.dog) Canvas(modifier = Modifier.fillMaxSize(), onDraw = { drawImage(dogImage) })
ImageBitmap ב-Canvas.ציור צורות בסיסיות
יש הרבה פונקציות לציור צורות ב-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() )
Path בהודעה חדשה.גישה לאובייקט 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() )
Drawable.מידע נוסף
למידע נוסף על ציור בכלי הכתיבה, אפשר להיעזר במקורות המידע הבאים:
- משני גרפיקה – מידע על הסוגים השונים של משני ציור.
- מכחול – כאן מוסבר איך להתאים אישית את הצביעה של התוכן.
- פריסות וגרפיקה בהתאמה אישית ב-Compose – Android Dev Summit 2022 – איך יוצרים ממשק משתמש בהתאמה אישית ב-Compose באמצעות פריסות וגרפיקה.
- JetLagged Sample – דוגמה ל-Compose שמראה איך לצייר תרשים בהתאמה אישית.
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- שינויים בגרפיקה
- גרפיקה ב-Compose
- קווי יישור ב-Jetpack פיתוח נייטיב