ConstraintLayout הוא פריסה שמאפשרת למקם רכיבים קומפוזביליים ביחס לרכיבים קומפוזביליים אחרים במסך. הוא מהווה חלופה לשימוש במספר רכיבים מקוננים של Row, Column, Box ורכיבי פריסה אחרים בהתאמה אישית.
במערכת View, ConstraintLayout הייתה הדרך המומלצת ליצור פריסות גדולות ומורכבות, כי היררכיית תצוגות שטוחה הייתה טובה יותר לביצועים מאשר תצוגות בתצוגות. עם זאת, ב-Compose אין בעיה כזו, כי הוא יכול לטפל ביעילות בהיררכיות פריסה עמוקות, ולכן ConstraintLayout לא מועיל באותה מידה.
רוצה להתחיל לצפות בתכנים של ConstraintLayout?
כדי להשתמש ב-ConstraintLayout ב-Compose, צריך להוסיף את התלות הזו בקובץ build.gradle (בנוסף להגדרת Compose):
implementation "androidx.constraintlayout:constraintlayout-compose:$constraintlayout_compose_version"
ConstraintLayout בפיתוח נייטיב פועל באופן הבא באמצעות DSL:
- יוצרים הפניות לכל רכיב Composable ב-
ConstraintLayoutבאמצעותcreateRefs()אוcreateRefFor(). - האילוצים מסופקים באמצעות המשתנה
constrainAs(), שמקבל את ההפניה כפרמטר ומאפשר לציין את האילוצים שלה בגוף הלמבדה. - מגדירים את האילוצים באמצעות
linkTo()או שיטות מועילות אחרות. -
parentהוא קובץ עזר קיים שאפשר להשתמש בו כדי לציין אילוצים לגבי ה-composable של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.
API מנותק
בדוגמה של ConstraintLayout, האילוצים מצוינים בתוך השורה, עם משתנה ברכיב הקומפוזבילי שעליו הם חלים. עם זאת, יש מצבים שבהם עדיף להפריד בין האילוצים לבין הפריסות שהם חלים עליהן.
לדוגמה, יכול להיות שתרצו לשנות את האילוצים בהתאם להגדרות המסך, או להוסיף אנימציה בין שתי קבוצות של אילוצים.
במקרים כאלה, אפשר להשתמש ב-ConstraintLayout בדרך אחרת:
- מעבירים
ConstraintSetכפרמטר אלConstraintLayout. - מקצים הפניות שנוצרו ב-
ConstraintSetלפונקציות הניתנות לשינוי באמצעות המשתנהlayoutId.
@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 מכיל מושגים כמו הנחיות, מחסומים ושרשראות שיכולים לעזור במיקום רכיבים בתוך ה-composable.
הנחיות
קווי עזר הם עזרים חזותיים קטנים שמאפשרים לעצב פריסות. אפשר להגביל רכיבים קומפוזביליים לקו עזר. קווי עזר שימושיים למיקום רכיבים בdp או בpercentage מסוימים בתוך הרכיב הניתן ליצירה (composable) ההורה.
יש שני סוגים של קווי עזר: אנכיים ואופקיים. שני הקווים האופקיים הם 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().
מחסומים
מחסומים מפנים לכמה רכיבים קומפוזביליים כדי ליצור קו מנחה וירטואלי שמבוסס על הווידג'ט הקיצוני ביותר בצד שצוין.
כדי ליצור מחסום, משתמשים ב-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: המרווח מחולק באופן שווה בין כל הרכיבים הניתנים לקישור, בלי מרווח פנוי לפני הרכיב הראשון או אחרי הרכיב האחרון. -
ChainStyle.Packed: המרווח מתווסף לפני הרכיב הראשון ואחרי הרכיב האחרון, והרכיבים נארזים יחד בלי מרווח ביניהם.