אחד מהכללים של Compose הוא שצריך למדוד את הצאצאים רק פעם אחת. מדידת צאצאים פעמיים גורמת לחריגה בזמן הריצה. אבל לפעמים צריך לקבל מידע על הצאצאים לפני שמודדים אותם.
בעזרת Intrinsics אפשר לשלוח שאילתות לצאצאים לפני שהם נמדדים בפועל.
אפשר לבקש מפונקציה קומפוזבילית את ה-IntrinsicSize.Min או ה-IntrinsicSize.Max שלה:
Modifier.width(IntrinsicSize.Min)– מה הרוחב המינימלי שנדרש כדי להציג את התוכן כמו שצריך?Modifier.width(IntrinsicSize.Max)– מה הרוחב המקסימלי שנדרש כדי להציג את התוכן כמו שצריך?Modifier.height(IntrinsicSize.Min)– מה הגובה המינימלי שנדרש כדי להציג את התוכן כמו שצריך?Modifier.height(IntrinsicSize.Max)– מה הגובה המקסימלי שנדרש כדי להציג את התוכן כמו שצריך?
לדוגמה, אם שואלים את minIntrinsicHeight של Text עם אילוצים אינסופיים של width בפריסה מותאמת אישית, הוא מחזיר את height של Text כשהטקסט משורטט בשורה אחת.
התכונות המובנות בפעולה
אפשר ליצור רכיב Composable שמציג שני טקסטים במסך, שמופרדים באמצעות קו מפריד:
כדי לעשות את זה, משתמשים ב-Row עם שני רכיבי Text שניתנים ליצירה וממלאים את המקום הפנוי, וב-Divider באמצע. ה-Divider צריך להיות בגובה של ה-Text הכי גבוה, והוא צריך להיות דק (width = 1.dp).
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) VerticalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } }
ה-Divider מתרחב לכל המסך, וזו לא ההתנהגות הרצויה:
זה קורה כי Row מודד כל צאצא בנפרד, ואי אפשר להשתמש בגובה של Text כדי להגביל את Divider.
כדי שה-Divider ימלא את המרחב הזמין בגובה נתון, משתמשים במשתנה height(IntrinsicSize.Min).
הגובה של הצאצאים של height(IntrinsicSize.Min) יהיה הגובה הפנימי המינימלי שלהם. מכיוון שהמשתנה הזה הוא רקורסיבי, הוא שולח שאילתה ל-minIntrinsicHeight של Row ושל הצאצאים שלו.
כדי שהקוד יפעל כמו שצריך, צריך להוסיף לו את המאפיין הזה:
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier.height(IntrinsicSize.Min)) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) VerticalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } } // @Preview @Composable fun TwoTextsPreview() { MaterialTheme { Surface { TwoTexts(text1 = "Hi", text2 = "there") } } }
עם תצוגה מקדימה:
הגובה של Row נקבע באופן הבא:
- ה-
minIntrinsicHeightשל ה-composableRowהוא ה-minIntrinsicHeightהמקסימלי של הצאצאים שלו. - הערך של
minIntrinsicHeightברכיבDividerהוא 0, כי הוא לא תופס מקום אם לא מוגדרות מגבלות. - השדה
TextminIntrinsicHeightהוא של הטקסט שלwidthספציפי. - לכן, האילוץ
heightשל הרכיבRowהופך לערך המקסימלי שלminIntrinsicHeightשל ה-Text. - לאחר מכן, ה-
Dividerמרחיב את ה-heightשלו ל-heightשל האילוץ שניתן על ידי ה-Row.
מאפיינים פנימיים בפריסות בהתאמה אישית
כשיוצרים משתנה משנה מותאם אישית של Layout או layout, המידות הפנימיות מחושבות באופן אוטומטי על סמך הערכות. לכן, יכול להיות שהחישובים לא יהיו נכונים לכל הפריסות. ממשקי ה-API האלה כוללים אפשרויות לשינוי ברירות המחדל האלה.
כדי לציין את המידות הפנימיות של Layout בהתאמה אישית, צריך לשנות את הערכים של minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth ו-maxIntrinsicHeight בממשק MeasurePolicy כשיוצרים אותו.
ההגדרה החדשה צריכה להתאים לפעולה של measure. לדוגמה, בעמודה בהתאמה אישית שבה הצאצאים מוערמים אנכית וכל צאצא מקבל את רוחב העמודה המלא, הגובה הפנימי המינימלי של width נתון הוא סכום הגבהים הפנימיים המינימליים של הצאצאים שלו עבור אותו width:
@Composable fun MyCustomComposable( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( content = content, modifier = modifier, measurePolicy = object : MeasurePolicy { override fun MeasureScope.measure( measurables: List<Measurable>, constraints: Constraints ): MeasureResult { val childConstraints = constraints.copy(minHeight = 0) val placeables = measurables.map { measurable -> measurable.measure(childConstraints) } val width = constraints.constrainWidth( placeables.maxOfOrNull { it.width } ?: 0 ) val height = constraints.constrainHeight( placeables.sumOf { it.height } ) return layout(width, height) { var yPosition = 0 placeables.forEach { placeable -> placeable.placeRelative(x = 0, y = yPosition) yPosition += placeable.height } } } override fun IntrinsicMeasureScope.minIntrinsicHeight( measurables: List<IntrinsicMeasurable>, width: Int ): Int { return measurables.sumOf { measurable -> measurable.minIntrinsicHeight(width) } } // Other intrinsics related methods have a default value, // you can override only the methods that you need. } ) }
כשיוצרים משתנה layout מותאם אישית, צריך לשנות את השיטות הקשורות בממשק LayoutModifier. החישוב הפנימי צריך להתאים ל-measure. לדוגמה, עבור height נתון, משתנה שמוסיף מרווח פנימי קבוע בכל הצדדים מחסיר את המרווח הפנימי האנכי לפני שהוא שולח שאילתה ל-minIntrinsicWidth של הצאצא, ומוסיף את המרווח הפנימי האופקי לתוצאה:
fun Modifier.myCustomModifier(padding: Dp = 8.dp) = this then object : LayoutModifier { override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val paddingPx = padding.roundToPx() val horizontal = paddingPx * 2 val vertical = paddingPx * 2 val placeable = measurable.measure( constraints.offset(-horizontal, -vertical) ) val width = constraints.constrainWidth(placeable.width + horizontal) val height = constraints.constrainHeight(placeable.height + vertical) return layout(width, height) { placeable.place(paddingPx, paddingPx) } } override fun IntrinsicMeasureScope.minIntrinsicWidth( measurable: IntrinsicMeasurable, height: Int ): Int { val paddingPx = padding.roundToPx() val horizontal = paddingPx * 2 val vertical = paddingPx * 2 val childHeight = if (height == Constraints.Infinity) { Constraints.Infinity } else { (height - vertical).coerceAtLeast(0) } return measurable.minIntrinsicWidth(childHeight) + horizontal } // Other intrinsics related methods have a default value, // you can override only the methods that you need. }
מומלץ בשבילך
- הערה: טקסט הקישור מוצג כש-JavaScript מושבת
- פריסות בהתאמה אישית {:#custom-layouts}
- קווי יישור ב-Jetpack Compose
- שלבים ב-Jetpack Compose