מגבלות וסדר מגביל

ב-Compose, אפשר לשרשר יחד כמה משתני אופן פעולה כדי לשנות את המראה והתחושה של רכיב ה-Composable. שרשרות המשתנים האלה יכולות להשפיע על המגבלות שמועברות לרכיבים הניתנים לשילוב, שמגדירות את גבולות הגובה והרוחב.

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

גורמי שינוי בעץ של ממשק המשתמש

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

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

קוד של רכיבים מורכבים ורכיבי מודיפיקטור, והייצוג החזותי שלהם כעץ של ממשק משתמש.
איור 1. מודификаторים שמקיפים צמתים של פריסה בעץ של ממשק המשתמש.

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

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

כפי שמוצג באיור 2, ההטמעה של הרכיבים הניתנים לקישור Image ו-Text מורכבת משרשרת של מודיפיקרים שמקיפים צומת פריסה יחיד. ההטמעות של Row ו-Column הן פשוט צמתים של פריסה שמתארים איך לפרס את הצאצאים שלהם.

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

לסיכום:

  • מודификаторים עוטפים מודификатор יחיד או צומת פריסה יחיד.
  • צמתים של פריסה יכולים לפרס כמה צמתים צאצאים.

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

אילוצים בשלב הפריסה

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

  1. מדידת הצאצאים: הצומת מודד את הצאצאים שלו, אם יש כאלה.
  2. קביעת הגודל בעצמו: על סמך המדידות האלה, הצומת קובע את הגודל שלו.
  3. מיקום הצאצאים: כל צומת צאצא ממוקם ביחס למיקום של הצומת עצמו.

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

סוגי האילוצים

אילוצים יכולים להיות מהסוגים הבאים:

  • מוגבל: לצומת יש רוחב וגובה מקסימליים ומינימליים.
אילוצים מוגבלים בגדלים שונים בתוך מאגר.
איור 3. מגבלות מוגדרות.
  • ללא הגבלת גודל: הצומת לא מוגבל לגודל מסוים. גבולות הגובה והרוחב המקסימליים מוגדרים כ'ללא הגבלה'.
אילוצים ללא גבול שבהם הרוחב והגובה מוגדרים כ-infinity. האילוצים נמשכים מעבר לקונטיינר.
איור 4. אילוצים ללא גבול.
  • מדויק: הצומת מתבקש לעמוד בדרישת גודל מדויקת. הערכים המינימלי והמקסימלי מוגדרים לאותו ערך.
אילוצים מדויקים שתואמים לדרישת גודל מדויקת בתוך הקונטיינר.
איור 5. מגבלות מדויקות.
  • שילוב: הצומת פועל לפי שילוב של סוגי האילוצים שלמעלה. לדוגמה, אילוץ יכול להגביל את הרוחב תוך מתן אפשרות לגובה מקסימלי ללא הגבלה, או להגדיר רוחב מדויק אבל לספק גובה מוגבל.
שני מאגרים שמציגים שילובים של אילוצים מוגבלים ולא מוגבלים, ורוחב וגובה מדויקים.
איור 6. שילובים של אילוצים מוגבלים ולא מוגבלים, ורוחב וגובה מדויקים.

בקטע הבא מוסבר איך האילוצים האלה מועברים מהורה לצאצא.

איך האילוצים מועברים מהרכיב ההורה לרכיב הצאצא

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

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

באופן כללי, האלגוריתם פועל באופן הבא:

  1. כדי לקבוע את הגודל שהוא רוצה לתפוס, צומת הבסיס בעץ ממשק המשתמש מודד את הצאצאים שלו ומעביר את אותן אילוצים לצאצא הראשון שלו.
  2. אם הצאצא הוא שינוי שלא משפיע על המדידה, הוא מעביר את האילוצים לשינוי הבא. האילוצים מועברים במורד שרשרת המשתנים כפי שהם, אלא אם מגיעים למשתנה שמשפיע על המדידה. לאחר מכן, המערכת תשנה את הגודל של האילוצים בהתאם.
  3. כשמגיעים לצומת שאין לו צאצאים (נקרא "צומת עלה"), הוא קובע את הגודל שלו על סמך האילוצים שהועברו, ומחזיר את הגודל המחושב להורה שלו.
  4. הורה מתאים את האילוצים שלו על סמך המדידות של הצאצא הזה, ומפעיל את הצאצא הבא עם האילוצים המותאמים האלה.
  5. אחרי שכל הצאצאים של הצומת ההורה נמדדים, הצומת ההורה מחליט על הגודל שלו ומעביר את המידע הזה לצומת ההורה שלו.
  6. כך כל העץ עובר סריקה לפי עומק. בסופו של דבר, כל הצמתים מחליטים על הגדלים שלהם, ושלב המדידה מסתיים.

לדוגמה מפורטת, אפשר לצפות בסרטון Constraints and modifier order.

משתני אופן פעולה שמשפיעים על אילוצים

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

המשתנה size

המאפיין size מציין את הגודל המועדף של התוכן.

לדוגמה, יש ליצור את עץ ממשק המשתמש הבא בקונטיינר של 300dp על ידי 200dp. האילוצים מוגבלים, ומאפשרים רוחב בין 100dp ל-300dp וגובה בין 100dp ל-200dp:

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

המשתנה המשנה size מתאים את האילוצים הנכנסים כך שיתאימו לערך שהוענק לו. בדוגמה הזו, הערך הוא 150dp:

זהה לתרשים 7, מלבד שינוי הגודל שמתאים את האילוצים הנכנסים כך שיתאימו לערך שהוענק לו.
איור 8. המשתנה המשנה size שמתאים את האילוצים ל-150dp.

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

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

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

שרשרת של שני משתני גודל בעץ ממשק המשתמש והייצוג שלה בקונטיינר, שהוא התוצאה של הערך הראשון שהוענק ולא של הערך השני.
איור 10. שרשרת של שני משתני size, שבה הערך השני שהוענק (50dp) לא מבטל את הערך הראשון (100dp).

המשתנה requiredSize

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

כשהגודל מועבר בחזרה למעלה בעץ, הצומת הצאצא ממורכז במרחב הזמין:

המשתנה size והמשתנה requiredSize שמקושרים בעץ של ממשק המשתמש, והייצוג התואם בקונטיינר. האילוצים של המאפיין requiredSize מבטלים את האילוצים של המאפיין size.
איור 11. המשתנה המשנה requiredSize מבטל את האילוצים הנכנסים מהמשתנה המשנה size.

המשתנים width ו-height

המשתנה המשנה size מתאים את הרוחב והגובה של האילוצים. בעזרת המשתנה המשנה width אפשר להגדיר רוחב קבוע אבל להשאיר את הגובה לא מוגדר. באופן דומה, בעזרת המאפיין height אפשר להגדיר גובה קבוע, אבל להשאיר את הרוחב לא מוגדר:

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

המשתנה sizeIn

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

עץ ממשק משתמש עם המשתנה sizeIn עם הגדרות רוחב וגובה מינימלי ומקסימלי, והייצוג שלו בתוך מאגר.
איור 13. המשתנה המשנה sizeIn עם הערכים minWidth,‏ maxWidth,‏ minHeight ו-maxHeight.

דוגמאות

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

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .size(50.dp)
)

קטע הקוד הזה יוצר את הפלט הבא:

  • המאפיין fillMaxSize משנה את האילוצים כך שהרוחב והגובה המינימליים יוגדרו לערך המקסימלי – 300dp ברוחב ו-200dp בגובה.
  • גם אם המשתנה המשנה size רוצה להשתמש בגודל 50dp, הוא עדיין צריך לעמוד באילוצים המינימליים הנכנסים. לכן, המשתנה המשנה size יפיק גם את גבולות האילוצים המדויקים של 300 על ידי 200, ובאופן מעשי יתעלם מהערך שסופק במשתנה המשנה size.
  • Image פועל לפי המגבלות האלה ומדווח על גודל של 300 על 200, שמועברים עד לחלק העליון של העץ.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .fillMaxSize()
        .wrapContentSize()
        .size(50.dp)
)

קטע הקוד הזה יוצר את הפלט הבא:

  • המאפיין המשנה fillMaxSize מתאים את האילוצים כך שהרוחב והגובה המינימליים יהיו שווים לערך המקסימלי – 300dp ברוחב ו-200dp בגובה.
  • המשתנה המשנה wrapContentSize מאפס את האילוצים המינימליים. לכן, בעוד ש-fillMaxSize גרם להגבלות קבועות, wrapContentSize מאפס אותו חזרה להגבלות מוגבלות. הצומת הבא יכול עכשיו לתפוס שוב את כל המרחב, או להיות קטן יותר מהמרחב כולו.
  • המאפיין size מגדיר את המגבלות לגבולות המינימום והמקסימום של 50.
  • הערך של Image מוגדר לגודל 50 על 50, והתוסף size מעביר את הערך הזה.
  • למשתנה wrapContentSize יש מאפיין מיוחד. הוא לוקח את הצאצא שלו ומציב אותו במרכז של גבולות המינימום הזמינים שהועברו אליו. לכן, הגודל שהוא מעביר להורים שלו שווה למגבלות המינימליות שהועברו אליו.

שילוב של שלושה מודיפיקרים בלבד מאפשר להגדיר את הגודל של הרכיב הניתן לקיבוץ ולמקם אותו במרכז הרכיב ההורה.

Image(
    painterResource(R.drawable.hero),
    contentDescription = null,
    Modifier
        .clip(CircleShape)
        .padding(10.dp)
        .size(100.dp)
)

קטע הקוד הזה יוצר את הפלט הבא:

  • המשתנה clip לא משנה את האילוצים.
    • המשתנה padding מקטין את האילוצים המקסימליים.
    • המשתנה המשנה size מגדיר את כל האילוצים ל-100dp.
    • המאפיין Image מציית למגבלות האלה ומדווח על גודל של 100 על ידי 100dp.
    • המאפיין המשנה padding מוסיף 10dp לכל הגדלים, כך שהוא מגדיל את הרוחב והגובה שמדווחים ב-20dp.
    • בשלב הציור, המשתנה המשנה clip פועל על לוח של 120 לפי 120dp. לכן, יוצרים מסכה עגולה באותו גודל.
    • לאחר מכן, המשתנה המשנה padding גורם להכנסת התוכן שלו ב-10dp בכל הגדלים, כך שהוא מקטין את גודל ההדפסה על קנבס ל-100 ב-100dp.
    • ה-Image מצויר בלוח הציור הזה. התמונה נחתכת על סמך העיגול המקורי של 120dp, כך שהפלט הוא תוצאה לא עגולה.