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

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

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

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

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

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

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

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

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

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

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

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

לסיכום:

  • משתני ה-modifier עוטפים משתנה modifier או צומת פריסה יחיד.
  • צומתי פריסה יכולים לפרוס כמה צומתי צאצא.

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

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

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

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

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

סוגי המגבלות

אילוץ יכול להיות אחד מהבאים:

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

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

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

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

שרשרת של שני משתני גודל בעץ ממשק המשתמש והייצוג שלה במאגר, שהוא התוצאה של הערך הראשון שהועבר ולא הערך השני.
איור 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)
)

הפלט של קטע הקוד הזה הוא:

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

על ידי שילוב של שלושה משתני שינוי בלבד, אתם יכולים להגדיר גודל לרכיב ה-composable ולמרכז אותו ברכיב ההורה שלו.

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, כך שהתוצאה היא לא עגולה.