اصول چیدمان را بنویسید

جت‌پک کامپوز (Jetpack Compose) طراحی و ساخت رابط کاربری (UI) برنامه شما را بسیار آسان‌تر می‌کند. کامپوز حالت (state) را از طریق موارد زیر به عناصر رابط کاربری تبدیل می‌کند:

  1. ترکیب عناصر
  2. طرح‌بندی عناصر
  3. ترسیم عناصر

تبدیل حالت به رابط کاربری از طریق ترکیب، طرح‌بندی و ترسیم

این سند بر روی چیدمان عناصر تمرکز دارد و برخی از بلوک‌های سازنده‌ای که Compose برای کمک به شما در چیدمان عناصر رابط کاربری ارائه می‌دهد را توضیح می‌دهد.

اهداف طرح‌بندی‌ها در Compose

پیاده‌سازی سیستم طرح‌بندی Jetpack Compose دو هدف اصلی دارد:

مبانی توابع قابل ترکیب

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

یک تابع composable ممکن است چندین عنصر رابط کاربری را منتشر کند. با این حال، اگر در مورد نحوه چیدمان آنها راهنمایی ارائه ندهید، Compose ممکن است عناصر را به روشی که شما دوست ندارید، مرتب کند. برای مثال، این کد دو عنصر متنی تولید می‌کند:

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

بدون راهنمایی در مورد نحوه چیدمان آنها، Compose عناصر متن را روی هم قرار می‌دهد و آنها را غیرقابل خواندن می‌کند:

دو عنصر متنی که روی هم کشیده شده‌اند و متن را ناخوانا می‌کنند

Compose مجموعه‌ای از طرح‌بندی‌های آماده برای کمک به شما در چیدمان عناصر رابط کاربری ارائه می‌دهد و تعریف طرح‌بندی‌های تخصصی‌تر خودتان را آسان می‌کند.

اجزای طرح‌بندی استاندارد

در بسیاری از موارد، می‌توانید فقط از عناصر طرح‌بندی استاندارد Compose استفاده کنید.

Column برای قرار دادن موارد به صورت عمودی روی صفحه استفاده کنید.

@Composable
fun ArtistCardColumn() {
    Column {
        Text("Alfred Sisley")
        Text("3 minutes ago")
    }
}

دو عنصر متنی که در یک طرح ستونی مرتب شده‌اند، بنابراین متن قابل خواندن است

به طور مشابه، از Row برای قرار دادن آیتم‌ها به صورت افقی روی صفحه استفاده کنید. هر دو Column و Row از پیکربندی ترازبندی عناصری که در خود جای می‌دهند پشتیبانی می‌کنند.

@Composable
fun ArtistCardRow(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}

طرح‌بندی پیچیده‌تری را نشان می‌دهد، با یک گرافیک کوچک در کنار ستونی از عناصر متنی

از Box برای قرار دادن عناصر روی یکدیگر استفاده کنید. Box همچنین از پیکربندی ترازبندی خاص عناصر موجود در خود پشتیبانی می‌کند.

@Composable
fun ArtistAvatar(artist: Artist) {
    Box {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Icon(Icons.Filled.Check, contentDescription = "Check mark")
    }
}

دو عنصر را که روی هم قرار گرفته‌اند نشان می‌دهد

اغلب این بلوک‌های سازنده تنها چیزی هستند که نیاز دارید. می‌توانید تابع قابل ترکیب خودتان را بنویسید تا این طرح‌بندی‌ها را در یک طرح‌بندی پیچیده‌تر که مناسب برنامه شماست، ترکیب کند.

سه ترکیب‌بندی ساده‌ی طرح‌بندی را مقایسه می‌کند: ستون، سطر و جعبه

برای تنظیم موقعیت فرزندان در یک Row ، آرگومان‌های horizontalArrangement و verticalAlignment را تنظیم کنید. برای یک Column ، آرگومان‌های verticalArrangement و horizontalAlignment را تنظیم کنید:

@Composable
fun ArtistCardArrangement(artist: Artist) {
    Row(
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.End
    ) {
        Image(bitmap = artist.image, contentDescription = "Artist image")
        Column { /*...*/ }
    }
}

موارد در سمت راست تراز شده‌اند

مدل چیدمان

در مدل طرح‌بندی، درخت رابط کاربری در یک مرحله طرح‌بندی می‌شود. ابتدا از هر گره خواسته می‌شود که خودش را اندازه‌گیری کند، سپس هر فرزند را به صورت بازگشتی اندازه‌گیری می‌کند و محدودیت‌های اندازه را به پایین درخت به فرزندان منتقل می‌کند. سپس، گره‌های برگ اندازه‌گذاری و قرار داده می‌شوند، و اندازه‌ها و دستورالعمل‌های قرارگیری تعیین‌شده به بالای درخت ارسال می‌شوند.

به طور خلاصه، والدین قبل از فرزندانشان اندازه می‌گیرند، اما بعد از فرزندانشان سایزبندی و قرار داده می‌شوند.

تابع SearchResult زیر را در نظر بگیرید.

@Composable
fun SearchResult() {
    Row {
        Image(
            // ...
        )
        Column {
            Text(
                // ...
            )
            Text(
                // ...
            )
        }
    }
}

این تابع، درخت رابط کاربری زیر را تولید می‌کند.

SearchResult
  Row
    Image
    Column
      Text
      Text

در مثال SearchResult ، طرح درختی رابط کاربری از این ترتیب پیروی می‌کند:

  1. از Row گره ریشه خواسته می‌شود که اندازه‌گیری کند.
  2. گره ریشه Row از اولین فرزندش، Image ، می‌خواهد که اندازه‌گیری کند.
  3. Image یک گره برگ است (یعنی فرزندی ندارد)، بنابراین اندازه را گزارش می‌دهد و دستورالعمل‌های قرارگیری را برمی‌گرداند.
  4. گره ریشه Row از فرزند دوم خود، Column ، می‌خواهد که اندازه‌گیری کند.
  5. گره Column از اولین فرزند Text خود می‌خواهد که اندازه‌گیری کند.
  6. اولین گره Text یک گره برگ است، بنابراین اندازه را گزارش می‌دهد و دستورالعمل‌های قرارگیری را برمی‌گرداند.
  7. گره Column از فرزند دوم Text خود می‌خواهد که اندازه‌گیری کند.
  8. گره Text دوم یک گره برگ است، بنابراین اندازه را گزارش می‌دهد و دستورالعمل‌های قرارگیری را برمی‌گرداند.
  9. اکنون که گره Column ، فرزندان خود را اندازه‌گیری، تعیین اندازه و قرار داده است، می‌تواند اندازه و محل قرارگیری خود را تعیین کند.
  10. اکنون که گره ریشه Row فرزندان خود را اندازه‌گیری، تعیین اندازه و قرار داده است، می‌تواند اندازه و محل قرارگیری خود را تعیین کند.

ترتیب اندازه‌گیری، سایزبندی و قرارگیری در درخت رابط کاربری نتایج جستجو

عملکرد

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

اگر طرح‌بندی شما به دلایلی به چندین اندازه‌گیری نیاز دارد، Compose یک سیستم ویژه به نام اندازه‌گیری‌های ذاتی ارائه می‌دهد. می‌توانید اطلاعات بیشتر در مورد این ویژگی را در بخش اندازه‌گیری‌های ذاتی در طرح‌بندی‌های Compose مطالعه کنید.

از آنجایی که اندازه‌گیری و جایگذاری، زیرمرحله‌های مجزایی از مرحله طرح‌بندی هستند، هرگونه تغییری که فقط بر جایگذاری اقلام تأثیر بگذارد، نه اندازه‌گیری، می‌تواند جداگانه اجرا شود.

استفاده از اصلاح‌کننده‌ها در طرح‌بندی‌هایتان

همانطور که در بخش «اصلاح‌کننده‌های ترکیب» بحث شد، می‌توانید از اصلاح‌کننده‌ها برای تزئین یا تقویت ترکیب‌پذیرهای خود استفاده کنید. اصلاح‌کننده‌ها برای سفارشی‌سازی طرح‌بندی شما ضروری هستند. برای مثال، در اینجا ما چندین اصلاح‌کننده را برای سفارشی‌سازی ArtistCard به هم متصل می‌کنیم:

@Composable
fun ArtistCardModifiers(
    artist: Artist,
    onClick: () -> Unit
) {
    val padding = 16.dp
    Column(
        Modifier
            .clickable(onClick = onClick)
            .padding(padding)
            .fillMaxWidth()
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
        Spacer(Modifier.size(padding))
        Card(
            elevation = CardDefaults.cardElevation(defaultElevation = 4.dp),
        ) { /*...*/ }
    }
}

یک طرح‌بندی پیچیده‌تر، با استفاده از اصلاح‌کننده‌ها برای تغییر نحوه چیدمان گرافیک‌ها و اینکه کدام قسمت‌ها به ورودی کاربر پاسخ می‌دهند

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

  • clickable باعث می‌شود که یک composable به ورودی کاربر واکنش نشان دهد و یک موج نشان دهد.
  • padding اطراف یک عنصر فاصله ایجاد می‌کند.
  • fillMaxWidth باعث می‌شود که عنصر قابل ترکیب، حداکثر عرضی را که از والدش دریافت می‌کند، داشته باشد.
  • size() عرض و ارتفاع ترجیحی یک عنصر را مشخص می‌کند.

طرح‌بندی‌های قابل اسکرول

برای اطلاعات بیشتر در مورد طرح‌بندی‌های قابل اسکرول، به مستندات ژست‌های نوشتن (Compose gestures) مراجعه کنید.

برای لیست‌ها و لیست‌های تنبل، مستندات Compose lists را بررسی کنید.

طرح‌بندی‌های واکنش‌گرا

یک طرح‌بندی باید با در نظر گرفتن جهت‌گیری‌های مختلف صفحه نمایش و اندازه‌های مختلف فرم فاکتور طراحی شود. Compose چندین مکانیزم را به صورت پیش‌فرض ارائه می‌دهد تا تطبیق طرح‌بندی‌های قابل ترکیب شما با پیکربندی‌های مختلف صفحه نمایش را تسهیل کند.

محدودیت‌ها

برای اینکه محدودیت‌هایی که از والد می‌آیند را بشناسید و طرح‌بندی را بر اساس آنها طراحی کنید، می‌توانید از BoxWithConstraints استفاده کنید. محدودیت‌های اندازه‌گیری را می‌توانید در محدوده‌ی content lambda پیدا کنید. می‌توانید از این محدودیت‌های اندازه‌گیری برای ایجاد طرح‌بندی‌های مختلف برای پیکربندی‌های مختلف صفحه نمایش استفاده کنید:

@Composable
fun WithConstraintsComposable() {
    BoxWithConstraints {
        Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
    }
}

طرح‌بندی‌های مبتنی بر اسلات

Compose طیف وسیعی از composableها را بر اساس طراحی متریال با وابستگی androidx.compose.material:material (که هنگام ایجاد یک پروژه Compose در اندروید استودیو گنجانده شده است) ارائه می‌دهد تا ساخت رابط کاربری آسان شود. عناصری مانند Drawer ، FloatingActionButton و TopAppBar همگی ارائه شده‌اند.

کامپوننت‌های متریال از APIهای اسلات (slot APIs) به شدت استفاده می‌کنند، الگویی که Compose برای ایجاد لایه‌ای از سفارشی‌سازی بر روی composableها معرفی می‌کند. این رویکرد کامپوننت‌ها را انعطاف‌پذیرتر می‌کند، زیرا آنها یک عنصر فرزند را می‌پذیرند که می‌تواند خودش را پیکربندی کند، به جای اینکه مجبور باشد هر پارامتر پیکربندی فرزند را نمایش دهد. اسلات‌ها یک فضای خالی در رابط کاربری باقی می‌گذارند تا توسعه‌دهنده بتواند آن را به دلخواه پر کند. به عنوان مثال، اینها اسلات‌هایی هستند که می‌توانید در TopAppBar سفارشی کنید:

نموداری که اسلات‌های موجود در نوار برنامه Material Components را نشان می‌دهد

Composableها معمولاً یک lambda با قابلیت ترکیب content ( content: @Composable () -> Unit ) می‌گیرند. APIهای اسلات چندین پارامتر content را برای کاربردهای خاص ارائه می‌دهند. برای مثال، TopAppBar به شما امکان می‌دهد محتوای title ، navigationIcon و actions را ارائه دهید.

برای مثال، Scaffold به شما امکان می‌دهد یک رابط کاربری (UI) را با ساختار چیدمان اولیه Material Design پیاده‌سازی کنید. Scaffold اسلات‌هایی را برای رایج‌ترین کامپوننت‌های سطح بالای Material مانند TopAppBar ، BottomAppBar ، FloatingActionButton و Drawer فراهم می‌کند. با استفاده از Scaffold ، می‌توان به راحتی مطمئن شد که این کامپوننت‌ها به درستی در موقعیت مناسب قرار گرفته‌اند و به درستی با هم کار می‌کنند.

برنامه نمونه JetNews، که از Scaffold برای قرار دادن چندین عنصر استفاده می‌کند

@Composable
fun HomeScreen(/*...*/) {
    ModalNavigationDrawer(drawerContent = { /* ... */ }) {
        Scaffold(
            topBar = { /*...*/ }
        ) { contentPadding ->
            // ...
        }
    }
}

{% کلمه به کلمه %} {% فعل کمکی %} {% کلمه به کلمه %} {% فعل کمکی %}