עם Jetpack פיתוח נייטיב, קל יותר לעצב ולבנות את ממשק המשתמש של האפליקציה. Compose מבצע טרנספורמציה של מצב לרכיבי ממשק משתמש, באמצעות:
- הרכב של אלמנטים
- פריסת הרכיבים
- ציור של רכיבים
במאמר הזה נתמקד בפריסת הרכיבים, ונציג כמה מאבני הבניין ש-Compose מספקת כדי לעזור לכם לפרוס את רכיבי ממשק המשתמש.
מטרות הפריסות בכלי הכתיבה
ההטמעה של מערכת הפריסה ב-Jetpack Compose מבוססת על שתי מטרות עיקריות:
- ביצועים גבוהים
- אפשרות לכתוב בקלות פריסות בהתאמה אישית
מידע בסיסי על פונקציות הניתנות להגדרה
פונקציות הניתנות להגדרה הן אבן הבניין הבסיסית של Compose. פונקציה שאפשר להרכיב ממנה פונקציות אחרות היא פונקציה שפולטת Unit
ומתארת חלק כלשהו בממשק המשתמש. הפונקציה מקבלת קלט ומייצרת את מה שמוצג במסך. מידע נוסף על רכיבים מורכבים זמין במאמר בנושא מודל מנטלי של Compose.
פונקציה שאפשר להרכיב ממנה פונקציות אחרות יכולה להפיק כמה רכיבי ממשק משתמש. עם זאת, אם לא תספקו הנחיות לגבי אופן הסידור שלהם, יכול להיות ש-Compose יסדר את הרכיבים בצורה שלא תאהבו. לדוגמה, הקוד הזה יוצר שני רכיבי טקסט:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
אם לא תציינו איך אתם רוצים שהם יסודרו, הכלי ליצירת תמונות יערם את רכיבי הטקסט אחד על השני, כך שלא יהיה אפשר לקרוא אותם:
Compose מספקת אוסף של פריסות מוכנות לשימוש שיעזרו לכם לסדר את רכיבי ממשק המשתמש, ומאפשרת לכם להגדיר בקלות פריסות משלכם, שהן יותר ספציפיות.
רכיבים של פריסה רגילה
ברוב המקרים, אפשר פשוט להשתמש ברכיבי הפריסה הרגילים של 'יצירה'.
משתמשים ב-Column
כדי למקם פריטים אנכית במסך.
@Composable fun ArtistCardColumn() { Column { Text("Alfred Sisley") Text("3 minutes ago") } }
באופן דומה, משתמשים ב-Row
כדי למקם פריטים אופקית במסך. גם Column
וגם Row
תומכים בהגדרת היישור של הרכיבים שהם מכילים.
@Composable fun ArtistCardRow(artist: Artist) { Row(verticalAlignment = Alignment.CenterVertically) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { Text(artist.name) Text(artist.lastSeenOnline) } } }
אפשר להשתמש ב-Box
כדי להציב רכיבים אחד מעל השני. בנוסף, אפשר להגדיר ב-Box
יישור ספציפי של האלמנטים שהוא מכיל.
@Composable fun ArtistAvatar(artist: Artist) { Box { Image(bitmap = artist.image, contentDescription = "Artist image") Icon(Icons.Filled.Check, contentDescription = "Check mark") } }
לרוב, אבני הבניין האלה הן כל מה שצריך. אתם יכולים לכתוב פונקציה משלכם שאפשר להרכיב ממנה פריסות שונות, כדי ליצור פריסה מורכבת יותר שתתאים לאפליקציה שלכם.
כדי להגדיר את המיקום של הילדים בתוך Row
, מגדירים את הארגומנטים horizontalArrangement
ו-verticalAlignment
. כדי להגדיר Column
, צריך להגדיר את הארגומנטים verticalArrangement
ו-horizontalAlignment
:
@Composable fun ArtistCardArrangement(artist: Artist) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.End ) { Image(bitmap = artist.image, contentDescription = "Artist image") Column { /*...*/ } } }
מודל הפריסה
במודל הפריסה, עץ ממשק המשתמש נפרס במעבר יחיד. כל צומת מתבקש קודם למדוד את עצמו, ואז למדוד את כל צאצאיו באופן רקורסיבי, תוך העברת אילוצי גודל במורד העץ לצאצאים. לאחר מכן, גודלם של צמתי העלים נקבע והם ממוקמים, והגודל וההוראות למיקום שלהם מועברים חזרה במעלה העץ.
בקצרה, ההורים נמדדים לפני הילדים, אבל הם ממוקמים אחרי הילדים.
נניח שיש לכם את הפונקציה SearchResult
הבאה.
@Composable fun SearchResult() { Row { Image( // ... ) Column { Text( // ... ) Text( // ... ) } } }
הפונקציה הזו יוצרת את עץ ממשק המשתמש הבא.
SearchResult
Row
Image
Column
Text
Text
בדוגמה SearchResult
, הפריסה של עץ ממשק המשתמש היא לפי הסדר הזה:
- צומת הבסיס
Row
נמדד. - צומת הבסיס
Row
מבקש מהצאצא הראשון שלו,Image
, לבצע מדידה. -
Image
הוא צומת עלה (כלומר, אין לו צאצאים), ולכן הוא מדווח על גודל ומחזיר הוראות מיקום. - צומת הבסיס
Row
מבקש מהצאצא השני שלו,Column
, לבצע מדידה. - הצומת
Column
מבקש מהצאצא הראשון שלוText
לבצע מדידה. - הצומת הראשון
Text
הוא צומת עלה, ולכן הוא מדווח על גודל ומחזיר הוראות מיקום. - הצומת
Column
מבקש מהצאצא השני שלו,Text
, למדוד. - הצומת השני
Text
הוא צומת עלה, ולכן הוא מדווח על גודל ומחזיר הוראות מיקום. - אחרי שהצומת
Column
מדד את הצמתים הצאצאים, קבע את הגודל שלהם ומיקם אותם, הוא יכול לקבוע את הגודל והמיקום שלו. - אחרי שהצומת הבסיסי
Row
מדד את צאצאיו, קבע את הגודל שלהם ומיקם אותם, הוא יכול לקבוע את הגודל והמיקום שלו.
ביצועים
המודל של Compose משיג ביצועים גבוהים כי הוא מודד את הילדים רק פעם אחת. מדידה במעבר יחיד טובה לביצועים, ומאפשרת ל-Compose לטפל ביעילות בעצי ממשק משתמש עמוקים. אם רכיב מסוים מודד את רכיב הבן שלו פעמיים, ורכיב הבן מודד כל אחד מרכיבי הבן שלו פעמיים וכן הלאה, ניסיון יחיד לפריסת ממשק משתמש שלם יצטרך לבצע הרבה עבודה, ולכן יהיה קשה לשמור על ביצועים טובים של האפליקציה.
אם מסיבה כלשהי צריך כמה מידות לפריסה, ב-Compose יש מערכת מיוחדת, מידות פנימיות. מידע נוסף על התכונה הזו זמין במאמר מדידות פנימיות בפריסות של Compose.
מכיוון שהמדידה והמיקום הם שלבי משנה נפרדים של מעבר הפריסה, אפשר לבצע בנפרד שינויים שמשפיעים רק על המיקום של הפריטים ולא על המדידה.
שימוש במקשי צירוף בפריסות
כמו שמוסבר במאמר בנושא שינוי של פונקציות Compose, אפשר להשתמש במאפיינים כדי לשנות או לשפר את הפונקציות הניתנות להרכבה. המשנים חיוניים להתאמה אישית של פריסת התוכן. לדוגמה, כאן אנחנו משלבים כמה משנים כדי להתאים אישית את ArtistCard
:
@Composable fun ArtistCardModifiers( artist: Artist, onClick: () -> Unit ) { val padding = 16.dp Column( Modifier .clickable(onClick = onClick) .padding(padding) .fillMaxWidth() ) { Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ } Spacer(Modifier.size(padding)) Card( elevation = CardDefaults.cardElevation(defaultElevation = 4.dp), ) { /*...*/ } } }
בדוגמה של הקוד שלמעלה, אפשר לראות פונקציות שונות של שינוי שמשמשות יחד.
-
clickable
גורם לרכיב שאפשר להרכיב להגיב לקלט של משתמש ולהציג אפקט אדווה. -
padding
puts space around an element. - הפונקציה
fillMaxWidth
גורמת לרכיב הניתן להרכבה למלא את הרוחב המקסימלי שמוקצה לו מהרכיב ההורה. -
size()
מציין את הרוחב והגובה המועדפים של רכיב.
פריסות עם אפשרות גלילה
מידע נוסף על פריסות עם אפשרות גלילה זמין במסמכי התיעוד בנושא מחוות בכתיבה.
לרשימות ולרשימות עצלניות, אפשר לעיין במסמכים בנושא יצירת רשימות.
פריסות רספונסיביות
כשמעצבים פריסת תוכן, צריך לקחת בחשבון את כיווני המסך השונים ואת גדלי גורמי הצורה. ספריית Compose מציעה כמה מנגנונים שיעזרו לכם להתאים את פריסות ה-Composable לתצורות מסך שונות.
מגבלות
כדי לדעת מהן האילוצים שמגיעים מהרכיב האב ולתכנן את הפריסה בהתאם, אפשר להשתמש ב-BoxWithConstraints
. אילוצים של המדידה מופיעים בהיקף של פונקציית ה-Lambda של התוכן. אפשר להשתמש במגבלות המדידה האלה כדי ליצור פריסות שונות להגדרות מסך שונות:
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
פריסות מבוססות-משבצות
Compose מספקת מגוון רחב של פונקציות שאפשר להרכיב מהן ממשקי משתמש, שמבוססות על Material Design עם התלות androidx.compose.material:material
(שנכללת כשיוצרים פרויקט Compose ב-Android Studio), כדי להקל על בניית ממשקי משתמש. כל הרכיבים מסופקים, כמו
Drawer
,
FloatingActionButton
,
ו-TopAppBar
.
רכיבי Material משתמשים באופן נרחב בממשקי Slot API, תבנית ש-Compose מציגה כדי להוסיף שכבת התאמה אישית מעל רכיבים שאפשר להרכיב. הגישה הזו הופכת את הרכיבים לגמישים יותר, כי הם מקבלים רכיב צאצא שיכול להגדיר את עצמו במקום לחשוף כל פרמטר הגדרה של הצאצא.
המשבצות משאירות מקום ריק בממשק המשתמש שהמפתח יכול למלא כרצונו. לדוגמה, אלה המשבצות שאפשר להתאים אישית ב-TopAppBar
:
פונקציות Composable מקבלות בדרך כלל content
lambda של פונקציית Composable ( content: @Composable
() -> Unit
). ממשקי Slot API חושפים כמה פרמטרים של content
לשימושים ספציפיים.
לדוגמה, TopAppBar
מאפשר לכם לספק את התוכן עבור title
, navigationIcon
ו-actions
.
לדוגמה,
Scaffold
מאפשר לכם להטמיע ממשק משתמש עם מבנה פריסה בסיסי של Material Design.
Scaffold
מספק משבצות לרכיבי Material הנפוצים ביותר ברמה העליונה,
כמו TopAppBar
,
BottomAppBar
,
FloatingActionButton
ו-Drawer
. השימוש ב-Scaffold
מאפשר לוודא בקלות שהרכיבים האלה ממוקמים בצורה נכונה ופועלים יחד בצורה תקינה.
@Composable fun HomeScreen(/*...*/) { ModalNavigationDrawer(drawerContent = { /* ... */ }) { Scaffold( topBar = { /*...*/ } ) { contentPadding -> // ... } } }
מומלץ עבורך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- הרכבת מגבילים
- Kotlin ל-Jetpack פיתוח נייטיב
- רכיבי Material ופריסות