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

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

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

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

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

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

קוד של פונקציות composable ושל modifiers, והייצוג הוויזואלי שלהם כעץ של ממשק משתמש.
איור 1. ‫Modifiers wrapping layout nodes in the UI tree.

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

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

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

ההטמעות של Row ושל Column הן צמתי פריסה שמתארים איך לפרוס את צאצאיהם.

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

לסיכום:

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

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

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

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

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

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

סוגי מגבלות

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

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

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

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

במהלך השלב הראשון של האלגוריתם שמתואר במאמר Constraints in the layout phase, האילוצים מועברים מהרכיב האב לרכיב הבן בעץ ממשק המשתמש.

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

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

  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 מחליף את האילוצים הנכנסים ומעביר את הגודל שציינתם כגבולות מדויקים.

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

הגודל והמשנה 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)
)

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

ריבוע כחול שממלא את מאגר ההורה שלו.
איור 14. הגודל המקסימלי של Image נקבע על ידי שרשרת המשנים.
  • המשנה 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)
)

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

ריבוע כחול קטן במרכז של קונטיינר האב שלו.
איור 15. הפריט Image ממוקם במרכז וגודלו 50dp.
  • המשנה 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)
)

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

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