מידע בסיסי על הפריסה של הרכב

עם Jetpack פיתוח נייטיב קל יותר לעצב ולבנות את ממשק המשתמש של האפליקציה. Compose ממיר את המצב לרכיבי ממשק משתמש באמצעות:

  1. הרכבת רכיבים
  2. פריסה של רכיבים
  3. ציור רכיבים

הרכבת מצב טרנספורמציה לממשק משתמש באמצעות הרכבה, פריסה ושרטוט

המסמך הזה מתמקד בפריסת הרכיבים השונים, עם הסברים על חלק מאבני הבניין שפלטפורמת 'כתיבה' מספקת כדי לעזור לכם לפרוס את הרכיבים בממשק המשתמש.

מטרות הפריסות ב-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, פריסת העץ של ממשק המשתמש היא לפי הסדר הבא:

  1. צומת הרמה הבסיסית (root) Row מתבקש לבצע מדידה.
  2. צומת הבסיס Row מבקש מהצאצא הראשון שלו, Image, לבצע מדידה.
  3. Image הוא צומת עלה (כלומר, אין לו צאצאים), ולכן הוא מדווח על גודל ומחזיר הוראות מיקום.
  4. צומת הבסיס Row מבקש מהצאצא השני שלו, Column, לבצע מדידה.
  5. הצומת Column מבקש מהצאצא הראשון שלו מסוג Text לבצע מדידה.
  6. הצומת הראשון מסוג Text הוא צומת עלה, ולכן הוא מדווח על גודל ומחזיר הוראות מיקום.
  7. הצומת Column מבקש מהצאצא השני Text לבצע מדידה.
  8. הצומת השני של Text הוא צומת עלה, ולכן הוא מדווח על גודל ומחזיר הוראות מיקום.
  9. עכשיו, אחרי שהצומת Column מדד את הצאצאים שלו, קבע את הגודל שלהם והציב אותם, הוא יכול לקבוע את הגודל והמיקום שלו.
  10. עכשיו, אחרי שמדדנו את הגודל של הצאצאים של צומת הבסיס 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 מאפשר לרכיב ה-Composable להגיב לקלט של המשתמש ולהציג תנודות.
  • padding יוצר רווח מסביב לאובייקט.
  • fillMaxWidth גורם לרכיב ה-Composable למלא את הרוחב המקסימלי שהוגדר לו על ידי ההורה שלו.
  • size() מציין את הרוחב והגובה המועדפים של רכיב.

פריסות שניתן לגלול בהן

מידע נוסף על פריסות שניתן לגלול בהן זמין במסמכי התיעוד של תנועות הכתיבה.

לרשימת משימות ולרשימות אחרות

פריסות רספונסיביות

צריך לתכנן את הפריסה בהתאם לכיוונים שונים של המסך ולגדלים שונים של גורם הצורה. Compose מציע כמה מנגנונים מוכנים לשימוש שבעזרתם תוכלו להתאים את הפריסות הניתנות ליצירה להגדרות מסך שונות.

אילוצים

כדי לדעת מהם האילוצים שמגיעים מהרכיב ההורה ולתכנן את הפריסה בהתאם, אפשר להשתמש ב-BoxWithConstraints. מגבלות המדידה יכולות להיכלל בהיקף התוכן lambda. אפשר להשתמש במגבלות המדידה האלו כדי להרכיב פריסות שונות להגדרות שונות של המסך:

@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:

תרשים שמציג את מיקומי המשבצות הזמינים בסרגל האפליקציות של רכיבי Material Material

בדרך כלל, רכיבים מורכבים מקבלים פונקציית lambda מורכבת מסוג content ( content: @Composable () -> Unit). ממשקי API של משבצות חושפים כמה פרמטרים מסוג content לשימושים ספציפיים. לדוגמה, TopAppBar מאפשר לכם לספק את התוכן עבור title, navigationIcon ו-actions.

לדוגמה, Scaffold מאפשר להטמיע ממשק משתמש עם מבנה הפריסה הבסיסי של Material Design. Scaffoldמספק משבצות לרכיבים הנפוצים ביותר של Material, כמו TopAppBar, BottomAppBar, FloatingActionButton, ו-Drawer. באמצעות Scaffold קל לוודא שהרכיבים האלה ממוקמים בצורה נכונה ופועלים יחד בצורה תקינה.

אפליקציית הדוגמה JetNews, שמשתמשת ב-Scaffold כדי למקם כמה רכיבים

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}