אפליקציות רבות צריכות לשלוט בדיוק במה שמוצג במסך. זה יכול להיות פשוט כמו הצבת תיבה או עיגול על המסך במקום הנכון, או סידור מורכב של אלמנטים גרפיים בסגנונות שונים.
שרטוט בסיסי עם משנים ועם DrawScope
הדרך העיקרית לצייר משהו בהתאמה אישית ב-Compose היא באמצעות שינויים, כמו Modifier.drawWithContent, Modifier.drawBehind ו-Modifier.drawWithCache.
לדוגמה, כדי לצייר משהו מאחורי רכיב ה-Composable, אפשר להשתמש במאפיין 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 ) }
למידע נוסף על שינויים שונים בציורים, אפשר לעיין במסמכי העזרה בנושא שינויים בגרפיקה.
מערכת קואורדינטות
כדי לצייר משהו על המסך, צריך לדעת את ההיסט (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 מעלות.
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 או שאתם רוצים לצייר את הטקסט באופן ידני עם התאמה אישית, אתם יכולים להשתמש בשיטה 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 בלוח הציור.ציור צורות בסיסיות
יש הרבה פונקציות לציור צורות ב-DrawScope. כדי לצייר צורה, משתמשים באחת מפונקציות הציור המוגדרות מראש, כמו drawCircle:
val purpleColor = Color(0xFFBA68C8) Canvas( modifier = Modifier .fillMaxSize() .padding(16.dp), onDraw = { drawCircle(purpleColor) } )
API |
פלט |
|
|
|
|
|
|
|
|
|
|
|
|
|
שרטוט נתיב
נתיב הוא סדרה של הוראות מתמטיות שמובילות לציור אחרי ההפעלה. DrawScope יכול לשרטט נתיב באמצעות ה-method 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 2022 – איך ליצור ממשק משתמש בהתאמה אישית ב-Compose באמצעות פריסות וגרפיקה.
- JetLagged Sample – דוגמה ל-Compose שמראה איך לצייר תרשים בהתאמה אישית.
מומלץ בשבילכם
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- משני גרפיקה
- גרפיקה בפיתוח נייטיב
- קווי יישור ב-Jetpack Compose