בעזרת Jetpack Compose קל יותר לעצב ולפתח את ממשק המשתמש של האפליקציה. Compose ממיר את המצב לרכיבי ממשק משתמש באמצעות:
- הרכבת רכיבים
- פריסה של רכיבים
- ציור רכיבים
המסמך הזה מתמקד בפריסה של רכיבים, ומסביר על חלק מאבני הבניין ש-Compose מספק כדי לעזור לכם לתכנן את הפריסה של רכיבי ממשק המשתמש.
מטרות הפריסות ב-Compose
להטמעה של מערכת הפריסה ב-Jetpack Compose יש שתי מטרות עיקריות:
- ביצועים גבוהים
- אפשרות לכתוב בקלות פריסות בהתאמה אישית
יסודות של פונקציות הניתנות להגדרה
פונקציות הניתנות להגדרה הן אבני הבניין הבסיסיות של Compose. פונקציה שניתנת ליצירה היא פונקציה שמפיקה Unit
שמתארת חלק מממשק המשתמש. הפונקציה מקבלת קלט מסוים ויוצרת את מה שמוצג במסך. למידע נוסף על רכיבים שניתנים לשילוב, אפשר לעיין במסמכי העזרה בנושא המודל הקוגניטיבי של Compose.
פונקציה שניתנת ליצירה יכולה להפיק כמה רכיבי ממשק משתמש. עם זאת, אם לא תספקו הנחיות לגבי סדר הפריטים, יכול להיות שהם ימוקמו ב-Compose באופן שלא יתאים לכם. לדוגמה, הקוד הזה יוצר שני רכיבי טקסט:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
בלי הנחיות לגבי אופן הסדר שלהם, רכיבי הטקסט ב-Compose נערמים זה על גבי זה וקשה לקרוא אותם:
Compose כולל אוסף של פריסות מוכנות לשימוש שיעזרו לכם לסדר את רכיבי ממשק המשתמש, וגם מאפשר להגדיר בקלות פריסות משלכם שמתמקדות בצרכים ספציפיים.
רכיבי פריסה רגילים
במקרים רבים, אפשר פשוט להשתמש ברכיבי הפריסה הרגילים של 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
, פריסת העץ של ממשק המשתמש היא לפי הסדר הבא:
- צומת הרמה הבסיסית (root)
Row
מתבקש לבצע מדידה. - צומת הבסיס
Row
מבקש מהצאצא הראשון שלו,Image
, לבצע מדידה. Image
הוא צומת עלה (כלומר, אין לו צאצאים), ולכן הוא מדווח על גודל ומחזיר הוראות מיקום.- צומת הבסיס
Row
מבקש מהצאצא השני שלו,Column
, לבצע מדידה. - הצומת
Column
מבקש מהצאצא הראשון שלו מסוגText
לבצע מדידה. - הצומת הראשון מסוג
Text
הוא צומת עלה, ולכן הוא מדווח על גודל ומחזיר הוראות מיקום. - הצומת
Column
מבקש מהצאצא השניText
לבצע מדידה. - הצומת השני של
Text
הוא צומת עלה, ולכן הוא מדווח על גודל ומחזיר הוראות מיקום. - עכשיו, אחרי שהצומת
Column
מדד את הצאצאים שלו, קבע את הגודל שלהם והציב אותם, הוא יכול לקבוע את הגודל והמיקום שלו. - עכשיו, אחרי שמדדנו את הגודל של הצאצאים של צומת הבסיס
Row
, הנחנו אותם והחלטנו על המיקום שלהם, אפשר לקבוע את הגודל והמיקום של צומת הבסיס.
ביצועים
כדי להשיג ביצועים גבוהים, Compose מודד את הצאצאים רק פעם אחת. מדידה ב-Single-pass טובה לביצועים, ומאפשרת ל-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
יוצר רווח מסביב לאובייקט.fillMaxWidth
גורם לרכיב ה-Composable למלא את הרוחב המקסימלי שהוגדר לו על ידי ההורה שלו.size()
מציין את הרוחב והגובה המועדפים של רכיב.
פריסות שניתן לגלול בהן
מידע נוסף על פריסות שניתן לגלול בהן זמין במסמכי התיעוד של תנועות הכתיבה.
למידע נוסף על רשימות ורשימות עצלות, אפשר לעיין במסמכי העזרה בנושא כתיבת רשימות.
פריסות רספונסיביות
צריך לתכנן את הפריסה בהתאם לכיוונים שונים של המסך ולגדלים שונים של גורם הצורה. Compose מציע כמה מנגנונים מוכנים לשימוש שבעזרתם תוכלו להתאים את הפריסות הניתנות ליצירה להגדרות מסך שונות.
אילוצים
כדי לדעת מהם האילוצים שמגיעים מהרכיב ההורה ולתכנן את הפריסה בהתאם, אפשר להשתמש ב-BoxWithConstraints
. אילוצים למדידת הביצועים מופיעים בהיקף של פונקציית הלמה של התוכן. אפשר להשתמש באילוצים למדידת מודעות כדי ליצור פריסות שונות בהגדרות מסך שונות:
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
פריסות שמבוססות על משבצות
ב-Compose יש מגוון רחב של רכיבים שאפשר לשלב (composables) שמבוססים על Material Design, עם יחסי התלות androidx.compose.material:material
(שכלולים כשיוצרים פרויקט Compose ב-Android Studio) כדי שיהיה קל ליצור ממשקי משתמש. המערכת מספקת רכיבים כמו Drawer
, FloatingActionButton
ו-TopAppBar
.
רכיבי Material משתמשים הרבה בממשקי API של משבצות, דפוס ש-Compose מציג כדי להוסיף שכבת התאמה אישית מעל רכיבים שניתנים לשילוב. הגישה הזו מאפשרת ליצור רכיבים גמישים יותר, כי הם מקבלים רכיב צאצא שיכול להגדיר את עצמו במקום לחשוף כל פרמטר הגדרה של הצאצא.
משבצות משאירות מקום ריק בממשק המשתמש, והמפתח יכול למלא אותו כרצונו. לדוגמה, אלה הן המשבצות שאפשר להתאים אישית ב-TopAppBar
:
בדרך כלל, רכיבים מורכבים מקבלים פונקציית lambda מורכבת מסוג content
( content: @Composable
() -> Unit
). ממשקי 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 Design