ConstraintLayout היא פריסה שמאפשרת למקם רכיבים שניתנים להרכבה ביחס לרכיבים אחרים שניתנים להרכבה במסך. הוא מהווה חלופה לשימוש בכמה רכיבים מוטמעים של Row, Column, Box ופריסות מותאמות אישית אחרות.
במערכת התצוגה, ConstraintLayout הייתה הדרך המומלצת ליצור פריסות גדולות ומורכבות, כי היררכיית תצוגה שטוחה הייתה טובה יותר לביצועים מאשר תצוגות מקוננות. עם זאת, אין בעיה כזו ב-Compose, שיכולה לטפל ביעילות בהיררכיות עמוקות של פריסות, ולכן השימוש ב-ConstraintLayout לא מועיל במיוחד.
רוצה להתחיל לצפות בתכנים של ConstraintLayout?
כדי להשתמש ב-ConstraintLayout בכתיבת אימייל, צריך להוסיף את התלות הזו ב-build.gradle (בנוסף להגדרת Compose):
implementation "androidx.constraintlayout:constraintlayout-compose:$constraintlayout_compose_version"
ConstraintLayout ב-Compose פועל באופן הבא באמצעות DSL:
- יוצרים הפניות לכל רכיב שאפשר להרכיב ב-
ConstraintLayoutבאמצעותcreateRefs()אוcreateRefFor(). - האילוצים מסופקים באמצעות משנה ה-
constrainAs(), שמקבל את ההפניה כפרמטר ומאפשר לציין את האילוצים שלו ב-lambda של הגוף. - אילוצים מצוינים באמצעות
linkTo()או שיטות מועילות אחרות. -
parentהוא הפניה קיימת שאפשר להשתמש בה כדי לציין אילוצים לגבי הקומפוזביליConstraintLayoutעצמו.
דוגמה לרכיב שאפשר להרכיב ממנו ממשק משתמש באמצעות ConstraintLayout:
@Composable fun ConstraintLayoutContent() { ConstraintLayout { // Create references for the composables to constrain val (button, text) = createRefs() Button( onClick = { /* Do something */ }, // Assign reference "button" to the Button composable // and constrain it to the top of the ConstraintLayout modifier = Modifier.constrainAs(button) { top.linkTo(parent.top, margin = 16.dp) } ) { Text("Button") } // Assign reference "text" to the Text composable // and constrain it to the bottom of the Button composable Text( "Text", Modifier.constrainAs(text) { top.linkTo(button.bottom, margin = 16.dp) } ) } }
הקוד הזה מגביל את החלק העליון של Button לאב עם שוליים של 16.dp, ואת Text לחלק התחתון של Button גם עם שוליים של 16.dp.
Button ורכיב קומפוזבילי Text שמוגבלים אחד לשני ב-ConstraintLayout.
Decoupled API
בדוגמה של ConstraintLayout, המגבלות מצוינות בשורה, עם משנה ברכיב הקומפוזבילי שהן חלות עליו. עם זאת, יש מצבים שבהם עדיף להפריד את האילוצים מהפריסות שהם חלים עליהן.
לדוגמה, יכול להיות שתרצו לשנות את האילוצים בהתאם להגדרת המסך, או ליצור אנימציה בין שני סטים של אילוצים.
במקרים כאלה, אפשר להשתמש ב-ConstraintLayout בדרך אחרת:
- מעבירים את
ConstraintSetכפרמטר אלConstraintLayout. - משתמשים בשינוי
layoutIdכדי להקצות הפניות שנוצרו ב-ConstraintSetלרכיבים הניתנים להרכבה.
@Composable fun DecoupledConstraintLayout() { BoxWithConstraints { val constraints = if (minWidth < 600.dp) { decoupledConstraints(margin = 16.dp) // Portrait constraints } else { decoupledConstraints(margin = 32.dp) // Landscape constraints } ConstraintLayout(constraints) { Button( onClick = { /* Do something */ }, modifier = Modifier.layoutId("button") ) { Text("Button") } Text("Text", Modifier.layoutId("text")) } } } private fun decoupledConstraints(margin: Dp): ConstraintSet { return ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") constrain(button) { top.linkTo(parent.top, margin = margin) } constrain(text) { top.linkTo(button.bottom, margin) } } }
אחר כך, כשרוצים לשנות את האילוצים, פשוט מעבירים ConstraintSet אחר.
ConstraintLayout קונספטים
ConstraintLayout מכיל מושגים כמו הנחיות, מחסומים ושרשראות שיכולים לעזור במיקום רכיבים בתוך הקומפוזיציה.
הנחיות
ההנחיות הן עזרים חזותיים קטנים לעיצוב פריסות. אפשר להגביל את השימוש ברכיבי Composables להנחיה מסוימת. הנחיות שימושיות למיקום רכיבים במיקום מסוים dp או percentage בתוך רכיב ההורה הניתן להרכבה.
יש שני סוגים שונים של קווי הנחיה: אנכיים ואופקיים. שני הקווים האופקיים הם top ו-bottom, ושני הקווים האנכיים הם start ו-end.
ConstraintLayout { // Create guideline from the start of the parent at 10% the width of the Composable val startGuideline = createGuidelineFromStart(0.1f) // Create guideline from the end of the parent at 10% the width of the Composable val endGuideline = createGuidelineFromEnd(0.1f) // Create guideline from 16 dp from the top of the parent val topGuideline = createGuidelineFromTop(16.dp) // Create guideline from 16 dp from the bottom of the parent val bottomGuideline = createGuidelineFromBottom(16.dp) }
כדי ליצור הנחיה, משתמשים ב-createGuidelineFrom* עם סוג ההנחיה הנדרש. כך נוצרת הפניה שאפשר להשתמש בה בבלוק Modifier.constrainAs().
מחסומים
Barriers מפנה למספר רכיבים שאפשר להרכבה כדי ליצור קו מנחה וירטואלי על סמך הווידג'ט הקיצוני ביותר בצד שצוין.
כדי ליצור מחסום, משתמשים בתו createTopBarrier() (או: createBottomBarrier(),
createEndBarrier(), createStartBarrier()) ומספקים את קובצי העזר שמהם יורכב המחסום.
ConstraintLayout { val constraintSet = ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") val topBarrier = createTopBarrier(button, text) } }
אחר כך אפשר להשתמש במחסום בבלוק Modifier.constrainAs().
רשתות
שרשראות מספקות התנהגות שדומה לקבוצה בציר אחד (אופקית או אנכית). אפשר להגביל את הציר השני בנפרד.
כדי ליצור שרשרת, משתמשים ב-createVerticalChain או ב-createHorizontalChain:
ConstraintLayout { val constraintSet = ConstraintSet { val button = createRefFor("button") val text = createRefFor("text") val verticalChain = createVerticalChain(button, text, chainStyle = ChainStyle.Spread) val horizontalChain = createHorizontalChain(button, text) } }
אחר כך אפשר להשתמש בשרשרת בבלוק Modifier.constrainAs().
אפשר להגדיר שרשרת עם ChainStyles שונים, שקובעים איך לטפל ברווחים מסביב לרכיב שאפשר להרכיב, למשל:
-
ChainStyle.Spread: הרווח מחולק באופן שווה בין כל הרכיבים הניתנים להרכבה, כולל הרווח לפני הרכיב הראשון ואחרי הרכיב האחרון. -
ChainStyle.SpreadInside: הרווח מחולק באופן שווה בין כל רכיבי ה-Composable, בלי רווח חופשי לפני רכיב ה-Composable הראשון או אחרי רכיב ה-Composable האחרון. -
ChainStyle.Packed: הרווח מחולק לפני הרכיב הקומפוזבילי הראשון ואחרי האחרון, והרכיבים הקומפוזביליים צמודים זה לזה בלי רווחים ביניהם.