עם Jetpack Compose קל יותר לעצב ולבנות את ממשק המשתמש של האפליקציה. Compose מבצע טרנספורמציה של מצב לרכיבי ממשק משתמש, באמצעות:
- קומפוזיציה של אלמנטים
- פריסת הרכיבים
- ציור של רכיבים
במסמך הזה נתמקד בפריסת הרכיבים, ונציג כמה מאבני הבניין ש-Compose מספקת כדי לעזור לכם לפרוס את רכיבי ממשק המשתמש.
מטרות הפריסות ב-Compose
להטמעה של מערכת הפריסה ב-Jetpack Compose יש שתי מטרות עיקריות:
- ביצועים גבוהים
- אפשרות לכתוב בקלות פריסות בהתאמה אישית
היכרות עם פונקציות הניתנות להגדרה
פונקציות הניתנות להגדרה הן אבן הבניין הבסיסית של Compose. פונקציה קומפוזבילית היא פונקציה שפולטת Unit ומתארת חלק מממשק המשתמש. הפונקציה מקבלת קלט ומייצרת את מה שמוצג על המסך. מידע נוסף על רכיבים שאפשר להרכיב זמין במאמר בנושא מודל מנטלי של Compose.
פונקציה שאפשר להרכיב ממנה פונקציות אחרות יכולה להפיק כמה רכיבי ממשק משתמש. עם זאת, אם לא תספקו הנחיות לגבי אופן הסידור שלהם, יכול להיות שכלי הכתיבה יסדר את הרכיבים בצורה שלא תאהבו. לדוגמה, הקוד הזה יוצר שני רכיבי טקסט:
@Composable fun ArtistCard() { Text("Alfred Sisley") Text("3 minutes ago") }
אם לא תציינו איך אתם רוצים שהם יסודרו, 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") } }
לרוב, אבני הבניין האלה הן כל מה שצריך. אתם יכולים לכתוב פונקציה משולבת משלכם כדי לשלב את הפריסות האלה לפריסה מורכבת יותר שמתאימה לאפליקציה שלכם.
כדי להגדיר את המיקום של רכיב children בתוך 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.
מכיוון שהמדידה והמיקום הם שלבי משנה נפרדים של מעבר הפריסה, אפשר לבצע בנפרד שינויים שמשפיעים רק על מיקום הפריטים ולא על המדידה.
שימוש במקשי צירוף בפריסות
כפי שמוסבר במאמר בנושא שינוי התנהגות של פונקציות Composable, אפשר להשתמש ב-Modifiers כדי לשנות את המראה או את ההתנהגות של פונקציות Composable. המשנים חיוניים להתאמה אישית של הפריסה. לדוגמה, כאן אנחנו משלבים כמה משנים כדי להתאים אישית את 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יוצרת קומפוזיציה שמגיבה לקלט של המשתמש ומציגה אדווה. -
paddingputs space around an element. - הפונקציה
fillMaxWidthגורמת לרכיב הניתן להרכבה למלא את הרוחב המקסימלי שמוגדר לו מרכיב האב. -
size()מציין את הרוחב והגובה המועדפים של רכיב.
פריסות שניתן לגלול בהן
מידע נוסף על פריסות עם אפשרות גלילה זמין במסמכי התיעוד בנושא מחוות בכתיבה.
לרשימות ולרשימות עצלניות, אפשר לעיין במסמכים בנושא יצירת רשימות.
פריסות רספונסיביות
כשמעצבים פריסת תוכן, צריך לקחת בחשבון את כיווני המסך השונים ואת גדלי גורמי הצורה. ב-Compose יש כמה מנגנונים שעוזרים להתאים את הפריסות המודולריות להגדרות שונות של מסכים.
מגבלות
כדי לדעת מהם האילוצים שמגיעים מההורה ולתכנן את הפריסה בהתאם, אפשר להשתמש בBoxWithConstraints. אילוצים למדידה מופיעים בהיקף של פונקציית ה-lambda של התוכן. אפשר להשתמש במגבלות המדידה האלה כדי ליצור פריסות שונות להגדרות מסך שונות:
@Composable fun WithConstraintsComposable() { BoxWithConstraints { Text("My minHeight is $minHeight while my maxWidth is $maxWidth") } }
פריסות מבוססות-משבצות
Compose מספקת מגוון רחב של פונקציות Composable שמבוססות על 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 ופריסות