יישור קווים ב-Jetpack פיתוח נייטיב

מודל הפריסה של הכתיבה מאפשר להשתמש ב-AlignmentLine כדי ליצור קווים יישור לשימוש בפריסות ההורה כדי ליישר ולמקם את הקווים לילדים. לדוגמה, Row יכול להשתמש בקווים היישור המותאמים אישית של הצאצאים שלו כדי ליישר ביניהם.

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

חלק מהתוכן הקומפוזבילי ב'כתיבה' כבר כולל קווי יישור. לדוגמה, BasicText תוכן קומפוזבילי חושף את קווי היישור של FirstBaseline ו-LastBaseline.

בדוגמה שלמטה, LayoutModifier בהתאמה אישית שנקרא firstBaselineToTop קוראת את FirstBaseline כדי להוסיף מרווח פנימי אל Text החל מערך הבסיס הראשון.

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

fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp,
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // Check the composable has a first baseline
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}

@Preview
@Composable
private fun TextWithPaddingToBaseline() {
    MaterialTheme {
        Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
    }
}

כדי לקרוא את FirstBaseline בדוגמה, המאפיין placeable [FirstBaseline] נמצא בשימוש בשלב המדידה.

יצירת קווי יישור מותאמים אישית

כשיוצרים Layout בהתאמה אישית קומפוזבילי או מותאם אישית, LayoutModifier, אפשר לספק קווי יישור מותאמים אישית, כדי שתכנים קומפוזביליים אחרים ברמה העליונה יוכלו להשתמש בהם כדי ליישר ולמקם את הילדים שלהם בהתאם.

בדוגמה הבאה מוצג תוכן קומפוזבילי בהתאמה אישית BarChart שחושף שני תוכן קווים יישור, MaxChartValue ו-MinChartValue, כך שחומרים קומפוזביליים אחרים יכול להתאים לערך הנתונים המקסימלי והמינימלי של התרשים. שני טקסטים הרכיבים Max וmin, הותאמו למרכז של הרכיבים המותאמים אישית קווים ישרים.

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

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

/**
 * AlignmentLine defined by the maximum data value in a [BarChart]
 */
private val MaxChartValue = HorizontalAlignmentLine(merger = { old, new ->
    min(old, new)
})

/**
 * AlignmentLine defined by the minimum data value in a [BarChart]
 */
private val MinChartValue = HorizontalAlignmentLine(merger = { old, new ->
    max(old, new)
})

קווי היישור המותאמים אישית שנועדו ליצור את הדוגמה שלנו הם מסוג HorizontalAlignmentLine, בשם הם משתמשים בהם ליישור אנכי של ילדים. מדיניות מיזוג מועברת בתור פרמטר למקרה שכמה פריסות מספקות ערך לקווי היישור האלה. בתור את הקואורדינטות של מערכת הפריסה של 'פיתוח נייטיב' ואת Canvas הקואורדינטות מייצגות את [0, 0], הפינה השמאלית העליונה והציר x ו-y הם חיובי כלפי מטה, כך שהערך של MaxChartValue יהיה תמיד קטן מ- MinChartValue. לכן, מדיניות המיזוג היא min עבור התרשים המקסימלי ו-max לערך הבסיס המינימלי של ערך הנתונים בתרשים.

כשיוצרים Layout או LayoutModifier בהתאמה אישית, צריך לציין התאמה אישית שורות בMeasureScope.layout , שלוקחת alignmentLines: Map<AlignmentLine, Int> הפרמטר.

@Composable
private fun BarChart(
    dataPoints: List<Int>,
    modifier: Modifier = Modifier,
) {
    val maxValue: Float = remember(dataPoints) { dataPoints.maxOrNull()!! * 1.2f }

    BoxWithConstraints(modifier = modifier) {
        val density = LocalDensity.current
        with(density) {
            // ...
            // Calculate baselines
            val maxYBaseline = // ...
            val minYBaseline = // ...
            Layout(
                content = {},
                modifier = Modifier.drawBehind {
                    // ...
                }
            ) { _, constraints ->
                with(constraints) {
                    layout(
                        width = if (hasBoundedWidth) maxWidth else minWidth,
                        height = if (hasBoundedHeight) maxHeight else minHeight,
                        // Custom AlignmentLines are set here. These are propagated
                        // to direct and indirect parent composables.
                        alignmentLines = mapOf(
                            MinChartValue to minYBaseline.roundToInt(),
                            MaxChartValue to maxYBaseline.roundToInt()
                        )
                    ) {}
                }
            }
        }
    }
}

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

@Composable
private fun BarChartMinMax(
    dataPoints: List<Int>,
    maxText: @Composable () -> Unit,
    minText: @Composable () -> Unit,
    modifier: Modifier = Modifier,
) {
    Layout(
        content = {
            maxText()
            minText()
            // Set a fixed size to make the example easier to follow
            BarChart(dataPoints, Modifier.size(200.dp))
        },
        modifier = modifier
    ) { measurables, constraints ->
        check(measurables.size == 3)
        val placeables = measurables.map {
            it.measure(constraints.copy(minWidth = 0, minHeight = 0))
        }

        val maxTextPlaceable = placeables[0]
        val minTextPlaceable = placeables[1]
        val barChartPlaceable = placeables[2]

        // Obtain the alignment lines from BarChart to position the Text
        val minValueBaseline = barChartPlaceable[MinChartValue]
        val maxValueBaseline = barChartPlaceable[MaxChartValue]
        layout(constraints.maxWidth, constraints.maxHeight) {
            maxTextPlaceable.placeRelative(
                x = 0,
                y = maxValueBaseline - (maxTextPlaceable.height / 2)
            )
            minTextPlaceable.placeRelative(
                x = 0,
                y = minValueBaseline - (minTextPlaceable.height / 2)
            )
            barChartPlaceable.placeRelative(
                x = max(maxTextPlaceable.width, minTextPlaceable.width) + 20,
                y = 0
            )
        }
    }
}
@Preview
@Composable
private fun ChartDataPreview() {
    MaterialTheme {
        BarChartMinMax(
            dataPoints = listOf(4, 24, 15),
            maxText = { Text("Max") },
            minText = { Text("Min") },
            modifier = Modifier.padding(24.dp)
        )
    }
}