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

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

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

מגבילי התאמה הם אובייקטים רגילים של 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

כברירת מחדל, פריסות שמופיעות ב'כתיבה' כוללות את הצאצאים שלהן. אבל, לפעמים אפשר להגדיר גודל באמצעות מקש הצירוף 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
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. כדי לקבל מידע מעמיק יותר על המקרים שבהם כדאי להשתמש בכל אחת מהאפשרויות האלה ואיך לבצע אופטימיזציה לביצועים, לקרוא ביצועי כתיבה – דחיית הקריאה כמה שיותר זמן.

היקף הבטיחות של הכתיבה

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

לדוגמה, אם רוצים להגדיר ילד או ילדה בגודל של הורה 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

כמו שראית בסעיף הקודם על ריפוד size, כברירת מחדל, גודל קומפוזבילי מוגדר על ידי את התוכן שעוטף. אפשר להגדיר גודל קומפוזבילי שיהיה גמיש במסגרת משתמש בתכונת הצירוף 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
    )
}

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

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

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.

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