چیدمان‌های سفارشی

در Compose، عناصر میانای کاربر با توابع ترکیب‌پذیر نمایش داده می‌شوند که هنگام فراخوانی، بخشی از میانای کاربر را منتشر می‌کنند و سپس به درخت میانای کاربر اضافه می‌شوند که روی صفحه نمایش داده می‌شود. هر عنصر میانای کاربر یک والد و احتمالاً چندین فرزند دارد. هر عنصر نیز در داخل عنصر اصلی خود قرار دارد که با موقعیت (x, y) و اندازه مشخص می‌شود که به‌صورت width و height مشخص می‌شود.

والدین محدودیت‌های عناصر فرزند خود را تعریف می‌کنند. از عنصر خواسته می‌شود اندازه‌اش را در آن محدودیت‌ها تعریف کند. محدودیت‌ها حداقل و حداکثر width و height یک عنصر را محدود می‌کنند. اگر عنصری دارای عناصر فرزند باشد، ممکن است هر فرزند را برای کمک به تعیین اندازه آن اندازه‌گیری کند. وقتی عنصری اندازه خودش را تعیین و گزارش می‌کند، فرصت دارد نحوه قرار دادن عناصر فرزندش را نسبت به خودش تعریف کند، همان‌طور که به‌طور مفصل در ایجاد چیدمان‌های سفارشی شرح داده شده است.

چیدمان هر گره در درخت رابط کاربری فرایندی سه مرحله‌ای است. هر گره باید:

  1. اندازه‌گیری هر فرزند
  2. اندازه خودش را تعیین کند
  3. فرزندانش را قرار دهید
سه مرحله چیدمان گره: اندازه‌گیری فرزندان، تصمیم‌گیری درباره اندازه، قرار دادن فرزندان
شکل ۱. سه مرحله چیدمان گره عبارتند از اندازه‌گیری فرزندان، تصمیم‌گیری درباره اندازه، و قرار دادن فرزندان.

استفاده از دامنه‌ها تعیین می‌کند که چه زمانی می‌توانید فرزندانتان را اندازه‌گیری و جای‌گذاری کنید. اندازه‌گیری چیدمان فقط درطول مراحل اندازه‌گیری و چیدمان انجام می‌شود، و فرزند فقط درطول مراحل چیدمان (و فقط پس‌از اندازه‌گیری) می‌تواند قرار داده شود. به‌دلیل دامنه‌های Compose مانند MeasureScope، و PlacementScope، این مورد در زمان ترجمه اعمال می‌شود.

استفاده از اصلاح‌کننده چیدمان

می‌توانید از اصلاح‌کننده layout برای اصلاح نحوه اندازه‌گیری و چیدمان عنصر استفاده کنید. اصلاح‌کننده یک لامبدا می‌گیرد؛ پارامترهای آن شامل عنصری است که می‌توانید اندازه‌گیری کنید، که به‌عنوان measurable ارسال می‌شود، و محدودیت‌های ورودی آن composable، که به‌عنوان constraints ارسال می‌شود. یک اصلاح‌کننده چیدمان سفارشی می‌تواند به این شکل باشد:

fun Modifier.customLayoutModifier() =
    layout { measurable, constraints ->
        // ...
    }

Text را روی صفحه نمایش دهید و فاصله از بالا تا خط مبنای اولین خط نوشتار را کنترل کنید. این دقیقاً همان کاری است که اصلاح‌کننده paddingFromBaseline انجام می‌دهد؛ شما آن را در اینجا به‌عنوان نمونه پیاده‌سازی می‌کنید. برای انجام این کار، از اصلاح‌کننده layout استفاده کنید تا composable را به‌صورت دستی روی صفحه قرار دهید. در اینجا رفتار حاصل را می‌بینید که در آن حاشیه داخلی بالای Text روی 24.dp تنظیم شده است:

تفاوت بین حاشیه داخلی واسط کاربر عادی را که فضای بین عناصر را تنظیم می‌کند و حاشیه داخلی نوشتار را که فضای بین یک خط مبنا تا خط مبنای بعدی را تنظیم می‌کند نشان می‌دهد
شکل ۲. نوشتار با paddingFromBaseline اعمال شد.

کد تولید این فاصله‌گذاری در اینجا آمده است:

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)
    }
}

در این کد این اتفاق می‌افتد:

  1. در پارامتر لامبدا measurable، Text را که با پارامتر قابل‌اندازه‌گیری نشان داده می‌شود با فراخوانی measurable.measure(constraints) اندازه‌گیری می‌کنید.
  2. اندازه composable را با فراخوانی روش layout(width, height) مشخص می‌کنید که یک لامبدا نیز برای قرار دادن عناصر پیچیده‌شده ارائه می‌دهد. در این مورد، ارتفاع برابر است با ارتفاع Text به‌علاوه placeableY، که برابر است با firstBaselineToTop منهای اولین خط مبنای Text.
  3. با فراخوانی placeable.placeRelative(x, y)، عناصر پیچیده‌شده را در صفحه قرار می‌دهید. اگر عناصر پیچیده قرار داده نشوند، نمایان نخواهند بود. در چیدمان راست به چپ، placeRelative قرینه موقعیت x است؛ جهت چیدمان را ببینید. موقعیت y عبارت است از firstBaselineToTop منهای اولین خط مبنای نوشتار، به‌طوری‌که اولین خط مبنا firstBaselineToTop از بالا باشد.

برای درستی‌سنجی اینکه این مورد طبق انتظار کار می‌کند، از این اصلاح‌کننده در Text استفاده کنید:

@Preview
@Composable
fun TextWithPaddingToBaselinePreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
    }
}

@Preview
@Composable
fun TextWithNormalPaddingPreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.padding(top = 32.dp))
    }
}

پیش‌نمایش‌های متعدد از عناصر نوشتاری؛ یکی حاشیه داخلی معمولی بین عناصر را نشان می‌دهد، دیگری حاشیه داخلی از یک خط مبنا به خط مبنای بعدی را نشان می‌دهد.
شکل ۳. اصلاح‌کننده روی Text ترکیب‌پذیر اعمال شد و پیش‌نمایش آن نشان داده شد.

ایجاد طرح‌بندی‌های سفارشی

تغییردهنده layout فقط ترکیب‌پذیر فراخواننده را تغییر می‌دهد. برای اندازه‌گیری و چیدمان چندین ترکیب‌پذیر، از ترکیب‌پذیر Layout استفاده کنید. این ترکیب‌پذیر به شما امکان می‌دهد فرزندان را به‌صورت دستی اندازه‌گیری و چیدمان کنید. همه چیدمان‌های سطح بالاتر مثل Column و Row با ترکیب‌پذیر Layout ساخته شده‌اند.

این مثال نسخه بسیار پایه‌ای از Column را می‌سازد. اکثر چیدمان‌های سفارشی از این الگو پیروی می‌کنند:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // measure and position children given constraints logic here
        // ...
    }
}

مشابه اصلاح‌کننده layout، measurables فهرست فرزندانی است که باید اندازه‌گیری شوند و constraints محدودیت‌های والد است. با همان منطق قبلی، MyBasicColumn را می‌توان به‌صورت زیر پیاده‌سازی کرد:

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

ترکیب‌شدنی‌های فرزند با محدودیت‌های Layout ورودی محدود می‌شوند و براساس yPosition ترکیب‌شدنی قبلی قرار می‌گیرند.

نحوه استفاده از آن ترکیب‌پذیر سفارشی در اینجا آمده است:

@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
    MyBasicColumn(modifier.padding(8.dp)) {
        Text("MyBasicColumn")
        Text("places items")
        Text("vertically.")
        Text("We've done it by hand!")
    }
}

چند عنصر نوشتاری که در یک ستون روی هم قرار گرفته‌اند.
شکل ۴. پیاده‌سازی سفارشی Column.

جهت چیدمان

جهت چیدمان یک ترکیب‌شدنی را با تغییر دادن ترکیب محلی LocalLayoutDirection تغییر دهید.

If you're placing composables manually on the screen, the LayoutDirection is available from the layoutDirection property of the MeasureScope in the layout modifier or Layout composable.

هنگام استفاده از layoutDirection، ترکیب‌پذیرها را بااستفاده از place قرار دهید. برخلاف روش placeRelative ، place براساس جهت چیدمان (چپ به راست درمقابل راست به چپ) تغییر نمی‌کند.

طرح‌بندی‌های سفارشی در عمل

درباره چیدمان‌ها و اصلاح‌کننده‌ها در چیدمان‌های پایه در Compose بیشتر بدانید، و چیدمان‌های سفارشی را در عمل در نمونه‌های Compose که چیدمان‌های سفارشی ایجاد می‌کنند ببینید.

بیشتر بدانید

برای کسب اطلاعات بیشتر درباره چیدمان‌های سفارشی در Compose، به منابع تکمیلی زیر مراجعه کنید.

ویدیوها