Jetpack Compose طراحی و ساخت رابط کاربری اپلیکیشن را بسیار سادهتر میکند. Compose، State را از طریق مراحل زیر به عناصر UI تبدیل میکند:
این سند روی مرحله Layout عناصر تمرکز دارد و برخی از building blockهایی را توضیح میدهد که Compose برای چیدمان عناصر UI در اختیار شما قرار میدهد.
پیادهسازی سیستم Layout در Jetpack Compose دو هدف اصلی دارد:
نکته: در سیستم قدیمی Android View، هنگام تو در تو کردن برخی Viewها مانند RelativeLayout ممکن بود با مشکلات Performance مواجه شوید. اما Compose از چندبار اندازهگیری جلوگیری میکند، بنابراین میتوانید Layoutها را با عمق زیاد تو در تو کنید بدون اینکه عملکرد بهطور جدی تحت تأثیر قرار بگیرد.
توابع Composable، building block اصلی Compose هستند. یک تابع Composable تابعی است که Unit برمیگرداند و بخشی از UI شما را توصیف میکند. این تابع مقداری ورودی دریافت میکند و چیزی را که روی صفحه نمایش داده میشود تولید میکند.
یک تابع Composable ممکن است چندین عنصر UI تولید کند. با این حال، اگر مشخص نکنید این عناصر چگونه باید چیده شوند، Compose ممکن است آنها را به شکلی قرار دهد که مطلوب شما نباشد.
برای مثال، کد زیر دو عنصر Text ایجاد میکند:
@Composable
fun ArtistCard() {
Text("Alfred Sisley")
Text("3 minutes ago")
}
اگر مشخص نکنید این دو عنصر چگونه باید چیده شوند، Compose ممکن است Textها را روی یکدیگر قرار دهد و باعث شود خواندن آنها دشوار شود.
Compose مجموعهای از Layoutهای آماده را فراهم میکند تا بتوانید عناصر UI را بهدرستی مرتب کنید. همچنین ساخت Layoutهای سفارشی و تخصصیتر را نیز آسان میکند.
در بسیاری از موارد میتوانید فقط از عناصر استاندارد Layout در Compose استفاده کنید.
برای قرار دادن عناصر بهصورت عمودی در صفحه، از Column استفاده کنید:
@Composable
fun ArtistCardColumn() {
Column {
Text("Alfred Sisley")
Text("3 minutes ago")
}
}
به همین شکل، برای قرار دادن عناصر بهصورت افقی در صفحه از Row استفاده کنید.
هر دو Column و Row امکان تنظیم Alignment عناصر داخل خود را فراهم میکنند.
@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 همچنین امکان تنظیم Alignment مشخص برای عناصر داخل خود را فراهم میکند.@Composable
fun ArtistAvatar(artist: Artist) {
Box {
Image(bitmap = artist.image, contentDescription = "Artist image")
Icon(Icons.Filled.Check, contentDescription = "Check mark")
}
}
در بسیاری از مواقع همین building blockها کافی هستند. میتوانید تابع Composable مخصوص خود را بنویسید و این Layoutها را با هم ترکیب کنید تا Layout پیچیدهتری متناسب با اپلیکیشن خود ایجاد کنید.
نکته: Compose، Layoutهای تو در تو را بهصورت کارآمد مدیریت میکند و به همین دلیل این روش برای ساخت UIهای پیچیده مناسب است. این موضوع نسبت به Android Views یک بهبود محسوب میشود، چون در Android Views معمولاً برای جلوگیری از مشکلات عملکردی باید از Layoutهای تو در توی زیاد اجتناب میکردید.
برای تعیین موقعیت فرزندان داخل 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 { /*...*/ }
}
}
در مدل Layout، درخت UI در یک مرحله Layout میشود. ابتدا از هر Node خواسته میشود خودش را اندازهگیری کند، سپس بهصورت بازگشتی فرزندانش را اندازهگیری کند و محدودیتهای اندازه را به سمت پایین درخت منتقل کند.
سپس Leaf nodeها اندازهگذاری و در جای خود قرار داده میشوند و اندازههای نهایی و دستورهای Placement دوباره به سمت بالا درخت فرستاده میشوند.
بهصورت خلاصه:
تابع زیر را در نظر بگیرید:
@Composable
fun SearchResult() {
Row {
Image(
// ...
)
Column {
Text(
// ...
)
Text(
// ...
)
}
}
}
این تابع درخت UI زیر را ایجاد میکند:
SearchResult
Row
Image
Column
Text
Text
در مثال SearchResult، Layout درخت UI با ترتیب زیر انجام میشود:
Row خواسته میشود خودش را Measure کند.Row از اولین فرزند خود یعنی Image میخواهد Measure شود.Image یک Leaf node است، یعنی فرزندی ندارد، بنابراین اندازه خود را گزارش میدهد و دستورهای Placement را برمیگرداند.Row از دومین فرزند خود یعنی Column میخواهد Measure شود.Column از اولین فرزند Text خود میخواهد Measure شود.Text یک Leaf node است، بنابراین اندازه خود را گزارش میدهد و دستورهای Placement را برمیگرداند.Column از دومین فرزند Text خود میخواهد Measure شود.Text نیز یک Leaf node است و اندازه و دستورهای Placement را برمیگرداند.Column فرزندان خود را Measure، Size و Place کرده است، میتواند اندازه و موقعیت خودش را تعیین کند.Row فرزندان خود را Measure، Size و Place کرده است، میتواند اندازه و موقعیت خودش را مشخص کند.Compose با Measure کردن هر Child فقط یک بار، عملکرد بالایی ایجاد میکند.
Single-pass measurement برای Performance مناسب است و به Compose اجازه میدهد درختهای UI عمیق را بهصورت کارآمد مدیریت کند.
اگر یک عنصر Child خود را دو بار Measure کند و آن Child نیز هرکدام از فرزندانش را دو بار Measure کند و همین روند ادامه پیدا کند، یک بار Layout کردن کل UI نیاز به مقدار زیادی پردازش خواهد داشت و حفظ عملکرد مناسب اپلیکیشن سخت میشود.
اگر Layout شما به هر دلیلی به چند بار Measurement نیاز داشته باشد، Compose یک سیستم مخصوص به نام Intrinsic measurements ارائه میدهد.
از آنجا که Measurement و Placement دو زیرمرحله جداگانه از Layout pass هستند، تغییراتی که فقط روی Placement عناصر اثر میگذارند و Measurement را تغییر نمیدهند میتوانند بهصورت جداگانه اجرا شوند.
همانطور که در بخش Compose modifiers توضیح داده میشود، میتوانید از Modifierها برای تزئین یا گسترش قابلیتهای Composableها استفاده کنید.
Modifierها برای سفارشیسازی Layout بسیار مهم هستند.
برای مثال، در کد زیر چند Modifier بهصورت زنجیرهای برای سفارشیسازی 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),
) { /*...*/ }
}
}
در کد بالا چند تابع Modifier با هم استفاده شدهاند:
clickable باعث میشود یک Composable به ورودی کاربر واکنش نشان دهد و Ripple نمایش دهد.padding فضای اطراف یک عنصر را مشخص میکند.fillMaxWidth باعث میشود Composable حداکثر عرضی را که Parent در اختیار آن قرار داده پر کند.size() عرض و ارتفاع ترجیحی یک عنصر را مشخص میکند.نکته: Modifierها از بعضی جهات نقشی مشابه Layout parameterها در Layoutهای مبتنی بر View دارند. با این حال، چون برخی Modifierها مخصوص Scope مشخصی هستند، Type safety بیشتری ارائه میدهند و همچنین کمک میکنند راحتتر متوجه شوید چه قابلیتهایی برای یک Layout خاص قابل استفاده هستند.
در Layoutهای XML گاهی تشخیص اینکه یک Attribute مشخص برای یک View خاص قابل استفاده است یا نه دشوار است.
برای یادگیری بیشتر درباره Layoutهای قابل اسکرول، مستندات Gesture در Compose را بررسی کنید.
برای Listها و Lazy Listها نیز میتوانید مستندات List در Compose را ببینید.
یک Layout باید با توجه به جهتهای مختلف صفحه و اندازههای مختلف Form factor طراحی شود.
Compose چند مکانیزم آماده برای سازگار کردن Layoutهای Composable با Configurationهای مختلف صفحه فراهم میکند.
برای اینکه محدودیتهایی را که از Parent دریافت میکنید بدانید و Layout را بر اساس آن طراحی کنید، میتوانید از BoxWithConstraints استفاده کنید.
Measurement constraintها داخل Scope مربوط به Content lambda در دسترس هستند. میتوانید از این Constraintها برای ساخت Layoutهای مختلف در Configurationهای مختلف صفحه استفاده کنید:
@Composable
fun WithConstraintsComposable() {
BoxWithConstraints {
Text("My minHeight is $minHeight while my maxWidth is $maxWidth")
}
}
Compose با dependency مربوط به Material، مجموعه بزرگی از Composableهای مبتنی بر Material Design ارائه میدهد تا ساخت UI آسانتر شود.
عناصری مانند موارد زیر بهصورت آماده ارائه میشوند:
DrawerFloatingActionButtonTopAppBarComponentهای Material بهطور گسترده از Slot API استفاده میکنند. این یک الگو در Compose است که لایهای از سفارشیسازی را روی Composableها فراهم میکند.
این رویکرد Componentها را انعطافپذیرتر میکند، چون آنها یک عنصر Child دریافت میکنند که میتواند خودش را پیکربندی کند، بهجای اینکه Component مجبور باشد برای تمام تنظیمات ممکن Child یک پارامتر جداگانه ارائه دهد.
Slotها یک فضای خالی در UI ایجاد میکنند تا توسعهدهنده آن را به شکل دلخواه پر کند.
برای مثال، در TopAppBar میتوانید Slotهای مختلفی را سفارشی کنید.
Composableها معمولاً یک Composable lambda به نام content دریافت میکنند:
content: @Composable () -> Unit
Slot APIها چندین پارامتر content برای کاربردهای مشخص ارائه میکنند.
برای مثال، TopAppBar به شما اجازه میدهد محتواهای زیر را تعیین کنید:
titlenavigationIconactionsبهعنوان مثال، Scaffold به شما امکان میدهد یک UI را با ساختار اصلی Layout در Material Design پیادهسازی کنید.
Scaffold برای رایجترین Componentهای سطح بالای Material، Slotهایی ارائه میکند؛ مانند:
TopAppBarBottomAppBarFloatingActionButtonDrawerبا استفاده از Scaffold، راحتتر میتوانید مطمئن شوید این Componentها در محل مناسب قرار گرفتهاند و بهدرستی با یکدیگر کار میکنند.
@Composable
fun HomeScreen(/*...*/) {
ModalNavigationDrawer(drawerContent = { /* ... */ }) {
Scaffold(
topBar = { /*...*/ }
) { contentPadding ->
// ...
}
}
}
این محتوا کاملا رایگان توسط تیم کدلپر ترجمه شده و در اختیار شما کاربران عزیز قرار گرفته است، هر گونه کپی برداری برای مقاصد غیر رایگان و بدون ذکر منبع، مورد پیگیری قانونی قرار میگیرد.
ترجمه شده از منبع: منبع مستندات