באפליקציות רבות צריך להיות אפשר לשלוט באופן מדויק במה שמוצג במסך. זה יכול להיות משהו קטן כמו תיבה או עיגול על המסך במקום הנכון, או סידור מורכב של אלמנטים גרפיים בסגנונות שונים.
ציור בסיסי עם משתני שינוי ו-DrawScope
הדרך העיקרית לצייר משהו בהתאמה אישית ב-Compose היא באמצעות מגדירים, כמו
Modifier.drawWithContent,
Modifier.drawBehind ו-
Modifier.drawWithCache.
לדוגמה, כדי לצייר משהו מאחורי ה-composable, אפשר להשתמש ב-modifier drawBehind כדי להתחיל להריץ פקודות ציור:
Spacer( modifier = Modifier .fillMaxSize() .drawBehind { // this = DrawScope } )
אם אתם צריכים רק רכיב קומפוזבילי שמשרטט, אתם יכולים להשתמש ברכיב הקומפוזבילי Canvas. הרכיב הקומפוזבילי Canvas הוא wrapper נוח מסביב ל-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 ) } }
rotate() כדי להחיל סיבוב על היקף הציור הנוכחי, שמסובב את המלבן ב-45 מעלות.
inset
אפשר להשתמש ב-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 או רוצים לשרטט את הטקסט באופן ידני עם התאמה אישית, אתם יכולים להשתמש ב-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 שהוגדר. לדוגמה, הקוד הבא מעבד טקסט בשליש מהגובה ובשליש מהרוחב של האזור הקומפוזבילי, ומגדיר את 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 ב-Compose.גישה לאובייקט Canvas
ב-DrawScope, אין לכם גישה ישירה לאובייקט Canvas. אפשר להשתמש ב-DrawScope.drawIntoCanvas() כדי לקבל גישה לאובייקט Canvas עצמו, שאפשר להפעיל בו פונקציות.
לדוגמה, אם יש לכם Drawable בהתאמה אישית שאתם רוצים לצייר על ה-canvas, אתם יכולים לגשת ל-canvas ולקרוא ל-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, אפשר לעיין במקורות המידע הבאים:
- Graphics Modifiers – מידע ולמידה על הסוגים השונים של משתני השרטוט.
- מכחול – איך להתאים אישית את הצביעה של התוכן.
- פריסות וגרפיקה בהתאמה אישית ב-Compose – מפגש של מפתחי Android 2022 – איך ליצור ממשק משתמש בהתאמה אישית ב-Compose באמצעות פריסות וגרפיקה.
- JetLagged Sample – דוגמה ל-Compose שמראה איך לשרטט תרשים בהתאמה אישית.
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- משני גרפיקה
- גרפיקה ב-Compose
- קווי יישור ב-Jetpack Compose