שכבות ארכיטקטוניות ב-Jetpack פיתוח נייטיב

בדף הזה נספק סקירה כללית ברמה גבוהה של השכבות הארכיטקטוניות שמרכיבות את Jetpack Compose, ושל העקרונות המרכזיים שמנחים את העיצוב הזה.

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

  • שימוש ברמת הסתירה המתאימה ליצירת האפליקציה או הספרייה
  • מתי אפשר לרדת לרמה נמוכה יותר כדי לקבל יותר שליטה או התאמה אישית
  • צמצום יחסי התלות

שכבות

השכבות העיקריות של Jetpack פיתוח נייטיב הן:

איור 1. השכבות העיקריות של Jetpack פיתוח נייטיב.

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

זמן ריצה
המודול הזה מספק את העקרונות הבסיסיים של סביבת זמן הריצה של Compose, כמו remember,‏ mutableStateOf, ההערה @Composable ו-SideEffect. כדאי לפתח את השכבה הזו ישירות אם אתם צריכים רק את יכולות ניהול העצים של הכתיבה, ולא את ממשק המשתמש שלה.
UI
שכבת ממשק המשתמש מורכבת מכמה מודולים (ui-text,‏ ui-graphics,‏ ui-tooling וכו'). המודולים האלה מטמיעים את היסודות של ערכת הכלים לבניית ממשק משתמש, כמו LayoutNode, Modifier, רכיבי handler של קלט, פריסות בהתאמה אישית ושרטוט. כדאי להשתמש בשכבה הזו אם אתם צריכים רק את המושגים הבסיסיים של ערכת כלים לממשק משתמש.
Foundation
המודול הזה מספק אבני בניין לא תלויות במערכת עיצוב לממשק המשתמש של Compose, כמו Row ו-Column, LazyColumn, זיהוי של תנועות ספציפיות וכו'. מומלץ להשתמש בשכבת הבסיס הזו כדי ליצור מערכת עיצוב משלכם.
חומר
המודול הזה מספק הטמעה של מערכת Material Design לממשק המשתמש של Compose, עם מערכת נושאים, רכיבים מעוצבים, אינדיקציות של תנודות וסמלילים. התבססו על השכבה הזו כשמשתמשים ב-Material Design באפליקציה.

עקרונות עיצוב

אחד מהעקרונות המנחים של Jetpack Compose הוא לספק קטעי פונקציונליות קטנים וממוקדים שאפשר להרכיב (או ליצור מהם קומפוזיציה) יחד, במקום כמה רכיבים מונוליטיים. לגישה הזו יש כמה יתרונות.

בקרה

רכיבים ברמה גבוהה יותר נוטים לבצע יותר משימות, אבל מגבילים את מידת הבקרה הישירה שיש לכם. אם אתם צריכים יותר שליטה, תוכלו לפתוח את התפריט הנפתח כדי להשתמש ברכיב ברמה נמוכה יותר.

לדוגמה, אם רוצים ליצור אנימציה של הצבע של רכיב, אפשר להשתמש ב-API‏ animateColorAsState:

val color = animateColorAsState(if (condition) Color.Green else Color.Red)

עם זאת, אם אתם צריכים שהרכיב תמיד יתחיל באפור, אי אפשר לעשות זאת באמצעות ה-API הזה. במקום זאת, אפשר לעבור לרמה התחתונה של ה-API Animatable:

val color = remember { Animatable(Color.Gray) }
LaunchedEffect(condition) {
    color.animateTo(if (condition) Color.Green else Color.Red)
}

ממשק ה-API ברמה animateColorAsState מבוסס על ממשק ה-API ברמה Animatable. השימוש ב-API ברמה נמוכה יותר הוא מורכב יותר, אבל הוא מאפשר יותר שליטה. בוחרים את רמת ההפשטה שהכי מתאימה לצרכים שלכם.

התאמה אישית

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

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

Button מורכב מ-4 רכיבים:

  1. חומר Surface שמספק את הרקע, הצורה, טיפול בלחיצות וכו'.

  2. CompositionLocalProvider שמשנה את גרסת האלפא של התוכן כשהלחצן מופעל או מושבת.

  3. ProvideTextStyle מגדיר את סגנון ברירת המחדל של הטקסט שבו צריך להשתמש

  4. Row מספק את מדיניות ברירת המחדל של הפריסה לתוכן הלחצן

השמטנו כמה פרמטרים והערות כדי שהמבנה יהיה ברור יותר, אבל הרכיב כולו כולל רק כ-40 שורות קוד כי הוא פשוט מרכיב את 4 הרכיבים האלה כדי להטמיע את הלחצן. רכיבים כמו Button מגדירים מראש אילו פרמטרים הם חושפים, כדי לאזן בין האפשרות לבצע התאמות אישיות נפוצות לבין מצב שבו יש יותר מדי פרמטרים, דבר שעלול להקשות על השימוש ברכיב. לדוגמה, רכיבי Material Design מציעים התאמות אישיות שצוינו במערכת Material Design, כך שקל לפעול לפי עקרונות של Material Design.

עם זאת, אם רוצים לבצע התאמה אישית מעבר לפרמטרים של רכיב, אפשר לרדת רמה ולבצע יצירת ענף של רכיב. לדוגמה, לפי Material Design, ללחצנים צריך להיות רקע בצבע אחיד. אם אתם צריכים רקע עם שיפוע, הפרמטרים Button לא תומכים באפשרות הזו. במקרה כזה, תוכלו להשתמש בהטמעה של Material Button כקובץ עזר ולבנות רכיב משלכם:

@Composable
fun GradientButton(
    // …
    background: List<Color>,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(
                Brush.horizontalGradient(background)
            )
    ) {
        CompositionLocalProvider(/* … */) { // set material LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                content()
            }
        }
    }
}

בהטמעה שלמעלה נעשה שימוש ברכיבים משכבת ה-Material, כמו העקרונות של Material מסוג גרסת אלפא של תוכן נוכחי וסגנון הטקסט הנוכחי. עם זאת, הוא מחליף את החומר Surface ב-Row ומסדיר אותו כדי להשיג את המראה הרצוי.

אם אתם לא רוצים להשתמש בכלל במושגי Material, למשל אם אתם יוצרים מערכת עיצוב מותאמת אישית משלכם, תוכלו להשתמש רק ברכיבים של שכבת הבסיס:

@Composable
fun BespokeButton(
    // …
    backgroundColor: Color,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(backgroundColor)
    ) {
        // No Material components used
        content()
    }
}

Jetpack פיתוח נייטיב שומר את השמות הפשוטים ביותר לרכיבים ברמה הגבוהה ביותר. לדוגמה, androidx.compose.material.Text מבוסס על androidx.compose.foundation.text.BasicText. כך תוכלו לספק הטמעה משלכם עם השם הבולט ביותר, אם תרצו להחליף רמות גבוהות יותר.

בחירת רמת האבסטרקציה המתאימה

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

לדוגמה, אם רוצים להוסיף תמיכה בתנועות למרכיב מותאם אישית, אפשר ליצור אותו מאפס באמצעות Modifier.pointerInput, אבל יש רכיבים אחרים ברמה גבוהה יותר שנוצרו על סמך המרכיב הזה, שיכולים להוות נקודת התחלה טובה יותר. לדוגמה, Modifier.draggable,‏ Modifier.scrollable או Modifier.swipeable.

ככלל, מומלץ לבנות על הרכיב ברמה הגבוהה ביותר שמציע את הפונקציונליות הדרושה לכם, כדי ליהנות משיטות המומלצות שהוא כולל.

מידע נוסף

בדוגמה של Jetsnack מוסבר איך יוצרים מערכת עיצוב בהתאמה אישית.