תנאי הצירוף של הכתיבה

מגבילי התאמה מאפשרים לכם לקשט או לשפר תוכן קומפוזבילי. בעזרת המשתנים המשתנים תוכלו לבצע את הפעולות הבאות:

  • שינוי הגודל, הפריסה, ההתנהגות והמראה של התוכן הקומפוזבילי
  • הוספת מידע, כמו תוויות נגישות
  • עיבוד קלט של משתמשים
  • הוספת אינטראקציות ברמה גבוהה, כמו הפיכת אלמנט לקליקבילי, לגלילה, לגרירה או להגדלת התצוגה שלו

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

@Composable
private fun Greeting(name: String) {
    Column(modifier = Modifier.padding(24.dp)) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

שתי שורות טקסט על רקע צבעוני, עם מרווח פנימי סביב הטקסט.

אפשר לשרשר את הפונקציות האלה יחד כדי להרכיב אותן:

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

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

בקוד שלמעלה, שימו לב לפונקציות צירוף שונות שנעשה בהן שימוש יחד.

  • padding יוצר רווח מסביב לאובייקט.
  • fillMaxWidth הופך את המילוי הקומפוזבילי לרוחב המקסימלי שניתן לו ההורה שלו.

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

סדר ההתאמות חשוב

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

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

כל האזור, כולל הריפוד סביב הקצוות, מגיב לקליקים

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

@Composable
fun ArtistCard(/*...*/) {
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .clickable(onClick = onClick)
            .fillMaxWidth()
    ) {
        // rest of the implementation
    }
}

הרווח מסביב לקצה הפריסה לא מגיב יותר לקליקים

מקשי צירוף מובנים

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

padding וגם size

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

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(/*...*/)
        Column { /*...*/ }
    }
}

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

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.requiredSize(150.dp)
        )
        Column { /*...*/ }
    }
}

קובץ האימג' הצאצא גדול מהמגבלות שמגיעות מההורה שלו

בדוגמה הזו, גם כשההורה height מוגדר כ-100.dp, הגובה של הערך Image יהיה 150.dp, כי הצירוף requiredSize לוקח בעדיפות גבוהה.

אם רוצים שפריסת צאצא תמלא את כל הגובה הזמין שמותר לפי הורה, צריך להוסיף את הצירוף fillMaxHeight (התכונה 'כתיבה' מספקת fillMaxSize ו-fillMaxWidth):

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.size(width = 400.dp, height = 100.dp)
    ) {
        Image(
            /*...*/
            modifier = Modifier.fillMaxHeight()
        )
        Column { /*...*/ }
    }
}

גובה התמונה גדול כמו ההורה שלו

כדי להוסיף מרווח פנימי מסביב לרכיב, מגדירים מגביל padding.

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

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(
                text = artist.name,
                modifier = Modifier.paddingFromBaseline(top = 50.dp)
            )
            Text(artist.lastSeenOnline)
        }
    }
}

טקסט עם מרווח פנימי מעליו

היסט

כדי למקם פריסה ביחס למיקום המקורי שלה, מוסיפים את המשתנה offset ומגדירים את ההיסט בצייר x ו-y. קיזוזים יכולים להיות חיוביים ולא חיוביים. ההבדל בין padding לבין offset הוא שהוספת offset ל-composable לא משנה את המדידות שלו:

@Composable
fun ArtistCard(artist: Artist) {
    Row(/*...*/) {
        Column {
            Text(artist.name)
            Text(
                text = artist.lastSeenOnline,
                modifier = Modifier.offset(x = 4.dp)
            )
        }
    }
}

טקסט שנדחק לצד ימין של מאגר האב שלו

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

מקש הצירוף offset מספק שני עומסי יתר – offset שלוקח את מחלצת כפרמטרים ו-offset מקבלת lambda. כדי לקבל מידע מעמיק יותר על המקרים שבהם כדאי להשתמש בכל אחת מהאפשרויות האלה ואיך לבצע אופטימיזציה לביצועים, לקרוא ביצועי כתיבה – דחיית הקריאה כמה שיותר זמן.

אבטחת היקף ב-Compose

ב-Compose יש משתני אופן (modifiers) שאפשר להשתמש בהם רק כשהם חלים על צאצאים של רכיבים מסוימים ליצירה. Compose אוכף את הכלל הזה באמצעות היקפים מותאמים אישית.

לדוגמה, אם רוצים להגדיר ילד או ילדה בגודל של הורה Box בלי שמשפיע על הגודל של Box, יש להשתמש matchParentSize מגביל. matchParentSize זמין רק ב-BoxScope. לכן, אפשר להשתמש בו רק בנכס צאצא בתוך נכס הורה מסוג Box.

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

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

matchParentSize בעוד Box

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

לתשומת ליבכם, matchParentSize זמין רק במסגרת היקף של Box. כלומר, הוא חל רק על צאצאים ישירים של תכנים קומפוזביליים של Box.

בדוגמה הבאה, הצאצא Spacer מקבל את הגודל שלו ממאפיין ההורה Box, שבו הגודל שלו נקבע מהילדים הכי גדולים, ArtistCard במקרה הזה.

@Composable
fun MatchParentSizeComposable() {
    Box {
        Spacer(
            Modifier
                .matchParentSize()
                .background(Color.LightGray)
        )
        ArtistCard()
    }
}

רקע אפור שממלא את הקונטיינר שלו

אם נעשה שימוש ב-fillMaxSize במקום ב-matchParentSize, הפונקציה Spacer הייתה את כל השטח הזמין להורה, וכך גורמים להורה להרחיב ולמלא את כל השטח הזמין.

רקע אפור שממלא את המסך

weight ב-Row וב-Column

כפי שראיתם בקטע הקודם בנושא הגדרת שוליים וגודלים, כברירת מחדל, הגודל של רכיב שאפשר ליצור ממנו קומפוזיציות מוגדר לפי התוכן שהוא עוטף. אפשר להגדיר את הגודל של תוכן קומפוזבילי כך שיהיה גמיש בתוך האב שלו באמצעות המשתנה weight שזמין רק ב-RowScope וב-ColumnScope.

ניקח Row שמכיל שני רכיבים מורכבים מסוג Box. התיבה הראשונה מקבלת פעמיים את הערך weight של השנייה, אז היא מקבלת פעמיים את הערך רוחב. מכיוון ש-Row הוא 210.dp רחב, ה-Box הראשון הוא 140.dp רחב והשני הוא 70.dp:

@Composable
fun ArtistCard(/*...*/) {
    Row(
        modifier = Modifier.fillMaxWidth()
    ) {
        Image(
            /*...*/
            modifier = Modifier.weight(2f)
        )
        Column(
            modifier = Modifier.weight(1f)
        ) {
            /*...*/
        }
    }
}

רוחב התמונה הוא כפול רוחב טקסט

חילוץ ושימוש חוזר של מגבילי צירוף

אפשר לשרשר כמה צירופים כדי לקשט או להרחיב תוכן קומפוזבילי. הרשת הזו נוצרת דרך הממשק Modifier שמייצג רשימה ממוינת שלא ניתנת לשינוי של Modifier.Elements יחיד.

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

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

  • הקצאת המשתנים לא תתבצע מחדש כשיתבצע עיבוד מחדש של רכיבים מורכבים שמשתמשים בהם
  • שרשראות הצירוף עשויות להיות ארוכות ומורכבות מאוד, לכן שימוש חוזר אותו מופע של שרשרת יכול להקל את עומס העבודה שזמן הריצה של פיתוח נייטיב צריך כשמשווים ביניהם
  • החילוץ הזה עוזר לשפר את הניקיון, העקביות והתחזוקה של הקוד ב-codebase

שיטות מומלצות לשימוש חוזר במודיפיקורים

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

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

חילוץ של משתני אופן פעולה ושימוש חוזר בהם כשמנטרים מצב שמשתנה לעיתים קרובות

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

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // Creation and allocation of this modifier will happen on every frame of the animation!
        modifier = Modifier
            .padding(12.dp)
            .background(Color.Gray),
        animatedState = animatedState
    )
}

במקום זאת, אפשר ליצור את אותו מופע של הצירוף, לחלץ אותו ולהשתמש בו שוב ומעבירים אותו לתוכן הקומפוזבילי כך:

// Now, the allocation of the modifier happens here:
val reusableModifier = Modifier
    .padding(12.dp)
    .background(Color.Gray)

@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // No allocation, as we're just reusing the same instance
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

חילוץ של מגבילי התאמה ללא היקף ושימוש חוזר בהם

אפשר לבטל את ההיקף של המשתנים המשתנים או להגדיר את ההיקף שלהם ל-composable ספציפי. במקרה של משתני מודיפיקטור ללא היקף, אפשר לחלץ אותם בקלות מחוץ לרכיבים הניתנים לשילוב כמשתנים פשוטים:

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

@Composable
fun AuthorField() {
    HeaderText(
        // ...
        modifier = reusableModifier
    )
    SubtitleText(
        // ...
        modifier = reusableModifier
    )
}

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

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // ...
                modifier = reusableItemModifier,
            )
        }
    }
}

חילוץ של ערכי שינוי היקף ושימוש בהם שוב

כשאתם מתמודדים עם מגבילי התאמה בהיקף של תכנים קומפוזביליים מסוימים, אתם יכולים לחלץ אותן לרמה גבוהה ככל האפשר ולהשתמש שוב במקרים הרלוונטיים:

Column(/*...*/) {
    val reusableItemModifier = Modifier
        .padding(bottom = 12.dp)
        // Align Modifier.Element requires a ColumnScope
        .align(Alignment.CenterHorizontally)
        .weight(1f)
    Text1(
        modifier = reusableItemModifier,
        // ...
    )
    Text2(
        modifier = reusableItemModifier
        // ...
    )
    // ...
}

צריך להעביר את המשתנים המשתנים ברמת ההיקף שחולצו רק לצאצאים הישירים באותו היקף. ראו את הקטע בטיחות היקף ב- אפשר לקרוא פרטים נוספים על החשיבות של.

Column(modifier = Modifier.fillMaxWidth()) {
    // Weight modifier is scoped to the Column composable
    val reusableItemModifier = Modifier.weight(1f)

    // Weight will be properly assigned here since this Text is a direct child of Column
    Text1(
        modifier = reusableItemModifier
        // ...
    )

    Box {
        Text2(
            // Weight won't do anything here since the Text composable is not a direct child of Column
            modifier = reusableItemModifier
            // ...
        )
    }
}

שרשור נוסף של המשתנים שחולצו

אפשר להוסיף או לשרשר עוד שרשרות של משתני התאמה אישית שחולצו על ידי קריאה לפונקציה .then():

val reusableModifier = Modifier
    .fillMaxWidth()
    .background(Color.Red)
    .padding(12.dp)

// Append to your reusableModifier
reusableModifier.clickable { /*...*/ }

// Append your reusableModifier
otherModifier.then(reusableModifier)

חשוב לזכור שסדר המשתנים המשתנים חשוב!

מידע נוסף

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

לתרגול נוסף על אופן השימוש במקשי הצירוף, אפשר גם לעבור על פריסות בסיסיות ב-Compose codelab או עכשיו במאגר של Android.

למידע נוסף על משתני אופן התצוגה בהתאמה אישית ועל האופן שבו יוצרים אותם, תוכלו לעיין במאמר פריסות בהתאמה אישית – שימוש במשתנה אופן התצוגה.