Jetpack Compose یک جعبه ابزار UI مدرن برای اندروید است. نوشتن، نوشتن و حفظ رابط کاربری برنامه شما را با ارائه یک API اعلامی که به شما امکان می دهد رابط کاربری برنامه خود را بدون تغییر اجباری نماهای ظاهری ارائه دهید، آسان تر می کند. این اصطلاح نیاز به توضیح دارد، اما مفاهیم برای طراحی اپلیکیشن شما مهم هستند.
پارادایم برنامه نویسی اعلانی
از لحاظ تاریخی، سلسله مراتب نمای اندروید به عنوان درختی از ویجتهای UI قابل نمایش بوده است. از آنجایی که وضعیت برنامه به دلیل مواردی مانند تعاملات کاربر تغییر می کند، سلسله مراتب رابط کاربری باید برای نمایش داده های فعلی به روز شود. رایج ترین راه برای به روز رسانی رابط کاربری این است که با استفاده از توابعی مانند findViewById()
روی درخت راه بروید و با فراخوانی روش هایی مانند button.setText(String)
، container.addChild(View)
یا img.setImageBitmap(Bitmap)
گره ها را تغییر دهید. این روش ها وضعیت داخلی ویجت را تغییر می دهند.
دستکاری نماها به صورت دستی احتمال خطا را افزایش می دهد. اگر یک قطعه داده در چندین مکان ارائه شود، به راحتی فراموش می شود که یکی از نماهایی را که آن را نشان می دهد به روز کنید. ایجاد حالت های غیرقانونی نیز آسان است، زمانی که دو به روز رسانی به شیوه ای غیرمنتظره با هم تضاد داشته باشند. برای مثال، ممکن است یک بهروزرسانی سعی کند مقدار گرهای را که به تازگی از رابط کاربری حذف شده است، تنظیم کند. به طور کلی، پیچیدگی نگهداری نرم افزار با تعداد بازدیدهایی که نیاز به به روز رسانی دارند، افزایش می یابد.
در طی چندین سال گذشته، کل صنعت شروع به تغییر به سمت یک مدل UI اعلامی کرده است، که مهندسی مرتبط با ساخت و بهروزرسانی رابطهای کاربری را بسیار ساده میکند. این تکنیک با بازسازی مفهومی کل صفحه نمایش از ابتدا و سپس اعمال تنها تغییرات لازم کار می کند. این رویکرد از پیچیدگی بهروزرسانی دستی سلسله مراتب نمای حالتی جلوگیری میکند. Compose یک چارچوب رابط کاربری اعلامی است.
یک چالش با بازسازی کل صفحه نمایش این است که به طور بالقوه گران است، از نظر زمان، قدرت محاسباتی و مصرف باتری. برای کاهش این هزینه، Compose بهطور هوشمندانه انتخاب میکند که کدام بخش از UI باید در هر زمان معین دوباره ترسیم شود. همانطور که در Recomposition توضیح داده شد، این موضوع برای نحوه طراحی اجزای UI خود پیامدهایی دارد.
یک تابع قابل ترکیب ساده
با استفاده از Compose، می توانید رابط کاربری خود را با تعریف مجموعه ای از توابع ترکیبی که داده ها را می گیرند و عناصر UI را منتشر می کنند، بسازید. یک مثال ساده یک ویجت Greeting
است که یک String
را می گیرد و یک ویجت Text
را منتشر می کند که پیام تبریک را نمایش می دهد.
شکل 1. یک تابع ساده قابل ترکیب که داده ها را ارسال می کند و از آن برای ارائه یک ویجت متنی روی صفحه استفاده می کند.
چند نکته قابل توجه در مورد این تابع:
تابع با حاشیه نویسی
@Composable
حاشیه نویسی شده است. همه توابع Composable باید این حاشیه نویسی را داشته باشند. این حاشیه نویسی به کامپایلر Compose اطلاع می دهد که این تابع برای تبدیل داده ها به UI در نظر گرفته شده است.تابع داده ها را می گیرد. توابع قابل ترکیب می توانند پارامترهایی را بپذیرند که به منطق برنامه اجازه می دهد تا رابط کاربری را توصیف کند. در این حالت، ویجت ما یک
String
میپذیرد تا بتواند با نام کاربر را احوالپرسی کند.این تابع متن را در رابط کاربری نمایش می دهد. این کار را با فراخوانی تابع composable
Text()
انجام می دهد که در واقع عنصر UI متن را ایجاد می کند. توابع Composable سلسله مراتب UI را با فراخوانی سایر توابع قابل ترکیب منتشر می کنند.تابع چیزی را بر نمی گرداند. توابعی که UI منتشر می کنند نیازی به برگرداندن چیزی ندارند، زیرا آنها به جای ساختن ویجت های UI وضعیت صفحه مورد نظر را توصیف می کنند.
این عملکرد سریع، ناتوان و بدون عوارض جانبی است.
- عملکرد زمانی که چندین بار با یک آرگومان فراخوانی می شود یکسان عمل می کند و از مقادیر دیگری مانند متغیرهای سراسری یا فراخوانی
random()
استفاده نمی کند. - این تابع، UI را بدون هیچ گونه عوارض جانبی، مانند تغییر ویژگی ها یا متغیرهای سراسری، توصیف می کند.
به طور کلی، به دلایلی که در Recomposition بحث شد، همه توابع قابل ترکیب باید با این خصوصیات نوشته شوند.
- عملکرد زمانی که چندین بار با یک آرگومان فراخوانی می شود یکسان عمل می کند و از مقادیر دیگری مانند متغیرهای سراسری یا فراخوانی
تغییر پارادایم اعلامی
با بسیاری از ابزارهای ضروری UI شی گرا، شما UI را با نمونه سازی درختی از ویجت ها مقداردهی اولیه می کنید. شما اغلب این کار را با باد کردن یک فایل طرح بندی XML انجام می دهید. هر ویجت حالت داخلی خود را حفظ می کند و روش های گیرنده و تنظیم کننده را نشان می دهد که به منطق برنامه اجازه می دهد با ویجت تعامل داشته باشد.
در رویکرد اعلانی Compose، ویجت ها نسبتاً بدون حالت هستند و توابع تنظیم کننده یا دریافت کننده را نشان نمی دهند. در واقع، ویجت ها به عنوان اشیا در معرض دید قرار نمی گیرند. شما UI را با فراخوانی یک تابع قابل ترکیب با آرگومان های مختلف به روز می کنید. این امر ارائه حالت به الگوهای معماری مانند ViewModel
را آسان می کند، همانطور که در راهنمای معماری برنامه توضیح داده شده است. سپس، اجزای سازنده شما هر بار که دادههای قابل مشاهده بهروزرسانی میشوند، مسئول تبدیل وضعیت فعلی برنامه به یک رابط کاربری هستند.
شکل 2. منطق برنامه داده ها را به تابع ترکیبی سطح بالا ارائه می دهد. این تابع از دادهها برای توصیف رابط کاربری با فراخوانی سایر اجزای سازنده استفاده میکند و دادههای مناسب را به آن composableها و در پایین سلسله مراتب ارسال میکند.
هنگامی که کاربر با رابط کاربری تعامل می کند، رابط کاربری رویدادهایی مانند onClick
را افزایش می دهد. این رویدادها باید منطق برنامه را مطلع کنند، که سپس می تواند وضعیت برنامه را تغییر دهد. هنگامی که وضعیت تغییر می کند، توابع قابل ترکیب مجدد با داده های جدید فراخوانی می شوند. این باعث می شود که عناصر UI دوباره ترسیم شوند - این فرآیند ترکیب مجدد نامیده می شود.
شکل 3. کاربر با یک عنصر UI تعامل کرد و باعث ایجاد یک رویداد شد. منطق برنامه به رویداد پاسخ می دهد، سپس توابع قابل ترکیب به طور خودکار دوباره با پارامترهای جدید، در صورت لزوم، فراخوانی می شوند.
محتوای پویا
از آنجایی که توابع قابل ترکیب به جای XML در Kotlin نوشته می شوند، می توانند مانند هر کد دیگری پویا باشند. به عنوان مثال، فرض کنید می خواهید یک UI بسازید که به لیستی از کاربران خوش آمد بگوید:
@Composable fun Greeting(names: List<String>) { for (name in names) { Text("Hello $name") } }
این تابع لیستی از نام ها را می گیرد و برای هر کاربر یک تبریک ایجاد می کند. توابع قابل ترکیب می توانند بسیار پیچیده باشند. میتوانید از دستور if
استفاده کنید تا تصمیم بگیرید که آیا میخواهید عنصر UI خاصی را نشان دهید. می توانید از حلقه ها استفاده کنید. می توانید توابع کمکی را فراخوانی کنید. شما انعطاف پذیری کامل زبان اصلی را دارید. این قدرت و انعطافپذیری یکی از مزایای کلیدی Jetpack Compose است.
ترکیب مجدد
در یک مدل UI ضروری، برای تغییر یک ویجت، یک تنظیم کننده روی ویجت تماس می گیرید تا حالت داخلی آن را تغییر دهد. در Compose، تابع composable را دوباره با داده های جدید فراخوانی می کنید. انجام این کار باعث می شود که تابع دوباره ترکیب شود -- ویجت های ساطع شده توسط تابع در صورت لزوم با داده های جدید دوباره ترسیم می شوند. فریم ورک Compose می تواند به طور هوشمندانه تنها اجزایی را که تغییر کرده اند دوباره ترکیب کند.
به عنوان مثال، این تابع قابل ترکیب را در نظر بگیرید که یک دکمه را نمایش می دهد:
@Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }
هر بار که روی دکمه کلیک می شود، تماس گیرنده مقدار clicks
را به روز می کند. Compose دوباره لامبدا را با تابع Text
فراخوانی می کند تا مقدار جدید را نشان دهد. این فرآیند را ترکیب مجدد می نامند. سایر توابع که به مقدار بستگی ندارند دوباره ترکیب نمی شوند.
همانطور که بحث کردیم، ترکیب مجدد کل درخت رابط کاربری می تواند از نظر محاسباتی گران باشد، که از توان محاسباتی و عمر باتری استفاده می کند. Compose این مشکل را با این ترکیب مجدد هوشمند حل می کند.
بازترکیب فرآیند فراخوانی مجدد توابع ترکیبپذیر شما هنگام تغییر ورودیها است. این زمانی اتفاق می افتد که ورودی های تابع تغییر کند. وقتی Compose بر اساس ورودیهای جدید دوباره ترکیب میکند، فقط توابع یا لامبداهایی را که ممکن است تغییر کرده باشند فراخوانی میکند و بقیه را رد میکند. با نادیده گرفتن همه توابع یا لامبداهایی که پارامترهای تغییر یافته را ندارند، Compose میتواند بهطور مؤثری ترکیببندی مجدد کند.
هرگز به عوارض جانبی اجرای توابع قابل ترکیب وابسته نباشید، زیرا ممکن است از ترکیب مجدد یک تابع صرفنظر شود. اگر چنین کنید، کاربران ممکن است رفتارهای عجیب و غیرقابل پیش بینی را در برنامه شما تجربه کنند. یک اثر جانبی هر تغییری است که برای بقیه برنامه شما قابل مشاهده است. به عنوان مثال، این اقدامات همه عوارض جانبی خطرناکی هستند:
- نوشتن به یک ویژگی یک شی مشترک
- به روز رسانی یک قابل مشاهده در
ViewModel
- به روز رسانی تنظیمات برگزیده مشترک
توابع قابل ترکیب ممکن است به اندازه هر فریم، مانند زمانی که یک انیمیشن در حال ارائه است، دوباره اجرا شوند. توابع قابل ترکیب باید سریع باشند تا در طول انیمیشنها از jank جلوگیری شود. اگر نیاز به انجام عملیات گران قیمت دارید، مانند خواندن از ترجیحات مشترک، آن را در یک پسزمینه انجام دهید و نتیجه مقدار را بهعنوان پارامتر به تابع composable ارسال کنید.
به عنوان مثال، این کد یک composable برای به روز رسانی یک مقدار در SharedPreferences
ایجاد می کند. سازنده نباید از روی ترجیحات مشترک بخواند یا بنویسد. در عوض، این کد خواندن و نوشتن را به یک ViewModel
در یک برنامه پسزمینه منتقل میکند. منطق برنامه مقدار فعلی را با یک تماس برگشتی برای راه اندازی به روز رسانی ارسال می کند.
@Composable fun SharedPrefsToggle( text: String, value: Boolean, onValueChanged: (Boolean) -> Unit ) { Row { Text(text) Checkbox(checked = value, onCheckedChange = onValueChanged) } }
این سند تعدادی از مواردی را که باید هنگام استفاده از Compose از آنها آگاه بود را مورد بحث قرار می دهد:
- بازترکیب تا آنجا که ممکن است بسیاری از توابع و لامبداهای قابل ترکیب را نادیده می گیرد.
- ترکیب مجدد خوشبینانه است و ممکن است لغو شود.
- یک تابع قابل ترکیب ممکن است به دفعات به اندازه هر فریم از یک انیمیشن اجرا شود.
- توابع قابل ترکیب می توانند به صورت موازی اجرا شوند.
- توابع قابل ترکیب می توانند به هر ترتیبی اجرا شوند.
بخشهای زیر نحوه ساخت توابع قابل ترکیب برای پشتیبانی از ترکیب مجدد را پوشش میدهند. در هر مورد، بهترین روش این است که توابع قابل ترکیب خود را سریع، بی قدرت و بدون عوارض جانبی نگه دارید.
ترکیب مجدد تا آنجا که ممکن است حذف می شود
وقتی بخشهایی از UI شما نامعتبر است، Compose تمام تلاش خود را میکند تا فقط بخشهایی را که باید بهروزرسانی شوند، دوباره ترکیب کند. این بدان معنی است که ممکن است از اجرای مجدد یک Button's composable بدون اجرای هیچ یک از composable های بالا یا پایین آن در درخت UI صرفنظر کند.
هر تابع قابل ترکیب و لامبدا ممکن است به خودی خود دوباره ترکیب شود. در اینجا یک مثال آورده شده است که نشان می دهد چگونه ترکیب مجدد می تواند برخی از عناصر را هنگام ارائه یک لیست نادیده بگیرد:
/** * Display a list of names the user can click with a header */ @Composable fun NamePicker( header: String, names: List<String>, onNameClicked: (String) -> Unit ) { Column { // this will recompose when [header] changes, but not when [names] changes Text(header, style = MaterialTheme.typography.bodyLarge) HorizontalDivider() // LazyColumn is the Compose version of a RecyclerView. // The lambda passed to items() is similar to a RecyclerView.ViewHolder. LazyColumn { items(names) { name -> // When an item's [name] updates, the adapter for that item // will recompose. This will not recompose when [header] changes NamePickerItem(name, onNameClicked) } } } } /** * Display a single name the user can click. */ @Composable private fun NamePickerItem(name: String, onClicked: (String) -> Unit) { Text(name, Modifier.clickable(onClick = { onClicked(name) })) }
هر یک از این دامنه ها ممکن است تنها چیزی باشد که در طول یک ترکیب مجدد اجرا می شود. هنگام تغییر header
ممکن است نوشتن بدون اجرای هیچ یک از والدین آن به Column
لامبدا رد شود. و هنگام اجرای Column
، Compose ممکن است انتخاب کند که در صورت عدم تغییر names
، از موارد LazyColumn
رد شود.
باز هم، اجرای همه توابع قابل ترکیب یا لامبدا باید بدون عارضه باشد. هنگامی که نیاز به انجام یک عارضه جانبی دارید، آن را از پاسخ به تماس فعال کنید.
ترکیب مجدد خوش بینانه است
هر زمان که Compose فکر کند که پارامترهای یک composable ممکن است تغییر کرده باشد، recomposition شروع می شود. ترکیب مجدد خوشبینانه است، به این معنی که Compose انتظار دارد قبل از تغییر مجدد پارامترها، ترکیب مجدد را به پایان برساند. اگر پارامتری قبل از پایان ترکیب مجدد تغییر کند ، Compose ممکن است ترکیب مجدد را لغو کند و با پارامتر جدید مجدداً راه اندازی شود.
هنگامی که ترکیب مجدد لغو می شود، Compose درخت رابط کاربری را از ترکیب مجدد حذف می کند. اگر عوارض جانبی دارید که بستگی به رابط کاربری نمایش داده شده دارد، حتی اگر ترکیب لغو شود، این اثر جانبی اعمال خواهد شد. این می تواند به وضعیت برنامه ناسازگار منجر شود.
اطمینان حاصل کنید که همه توابع و لامبداهای قابل ترکیب فاقد قدرت و بدون عوارض جانبی برای مدیریت مجدد خوشبینانه هستند.
توابع قابل ترکیب ممکن است اغلب اجرا شوند
در برخی موارد، یک تابع قابل ترکیب ممکن است برای هر فریم از یک انیمیشن UI اجرا شود. اگر عملکرد عملیات گران قیمتی را انجام دهد، مانند خواندن از حافظه دستگاه، عملکرد می تواند باعث jank UI شود.
به عنوان مثال، اگر ویجت شما سعی کند تنظیمات دستگاه را بخواند، به طور بالقوه می تواند آن تنظیمات را صدها بار در ثانیه بخواند و اثرات مخربی بر عملکرد برنامه شما داشته باشد.
اگر تابع composable شما به داده نیاز دارد، باید پارامترهایی را برای داده ها تعریف کند. سپس می توانید کارهای گران قیمت را به رشته دیگری خارج از ترکیب بندی منتقل کنید و داده ها را با استفاده از mutableStateOf
یا LiveData
به Compose ارسال کنید.
توابع قابل ترکیب می توانند به صورت موازی اجرا شوند
Compose میتواند با اجرای موازی توابع قابل ترکیب، ترکیب مجدد را بهینه کند. این به Compose اجازه میدهد از چندین هسته استفاده کند و عملکردهای قابل ترکیب را بدون اولویت روی صفحه اجرا کند.
این بهینه سازی به این معنی است که یک تابع ترکیبی ممکن است در مجموعه ای از رشته های پس زمینه اجرا شود. اگر یک تابع composable تابعی را در ViewModel
فراخوانی کند، Compose ممکن است آن تابع را از چند رشته به طور همزمان فراخوانی کند.
برای اطمینان از اینکه برنامه شما به درستی رفتار می کند، همه توابع قابل ترکیب نباید هیچ گونه عوارض جانبی داشته باشند. درعوض، عوارض جانبی تماسهای برگشتی مانند onClick
را که همیشه در رشته رابط کاربری اجرا میشوند، ایجاد کنید.
هنگامی که یک تابع ترکیبی فراخوانی می شود، فراخوانی ممکن است در رشته ای متفاوت از تماس گیرنده رخ دهد. این بدان معناست که از کدهایی که متغیرها را در یک لامبدای ترکیبپذیر تغییر میدهند باید اجتناب شود – هم به این دلیل که چنین کدی امن نیست و هم به این دلیل که یک اثر جانبی غیرمجاز لامبدای ترکیبپذیر است.
در اینجا یک مثال نشان می دهد که یک composable را نشان می دهد که یک لیست و تعداد آن را نمایش می دهد:
@Composable fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } }
این کد بدون عوارض جانبی است و لیست ورودی را به UI تبدیل می کند. این کد عالی برای نمایش یک لیست کوچک است. با این حال، اگر تابع روی یک متغیر محلی بنویسد، این کد از نظر موضوعی امن یا صحیح نخواهد بود:
@Composable fun ListWithBug(myList: List<String>) { var items = 0 Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Card { Text("Item: $item") items++ // Avoid! Side-effect of the column recomposing. } } } Text("Count: $items") } }
در این مثال، items
با هر ترکیب مجدد اصلاح می شوند. این می تواند هر فریم از یک انیمیشن یا زمانی که لیست به روز می شود. در هر صورت، UI تعداد اشتباه را نمایش می دهد. به همین دلیل، نوشته هایی مانند این در Compose پشتیبانی نمی شوند. با ممنوع کردن این نوشتهها، به چارچوب اجازه میدهیم تا رشتهها را برای اجرای لامبداهای قابل ترکیب تغییر دهد.
توابع قابل ترکیب می توانند به هر ترتیبی اجرا شوند
اگر به کد یک تابع قابل ترکیب نگاه کنید، ممکن است فرض کنید که کد به ترتیب ظاهر شده اجرا می شود. اما این تضمینی برای صحت ندارد. اگر یک تابع قابل ترکیب شامل فراخوانیهای دیگر توابع قابل ترکیب باشد، آن توابع ممکن است به هر ترتیبی اجرا شوند. Compose این امکان را دارد که تشخیص دهد برخی از عناصر UI اولویت بیشتری نسبت به سایرین دارند و ابتدا آنها را ترسیم کند.
به عنوان مثال، فرض کنید کدی مانند این برای ترسیم سه صفحه در طرح بندی برگه دارید:
@Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }
تماسها با StartScreen
، MiddleScreen
و EndScreen
ممکن است به هر ترتیبی انجام شوند. این به این معنی است که شما نمیتوانید، برای مثال، StartScreen()
یک متغیر سراسری (عوارض جانبی) تنظیم کنید و MiddleScreen()
از آن تغییر استفاده کند. درعوض، هر یک از این توابع باید خودکفا باشند.
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد نحوه تفکر در توابع Compose و composable، منابع اضافی زیر را بررسی کنید.
ویدیوها
{% کلمه به کلمه %}برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- Kotlin برای Jetpack Compose
- State و Jetpack Compose
- لایه بندی معماری Jetpack Compose
Jetpack Compose یک جعبه ابزار UI مدرن برای اندروید است. نوشتن، نوشتن و حفظ رابط کاربری برنامه شما را با ارائه یک API اعلامی که به شما امکان می دهد رابط کاربری برنامه خود را بدون تغییر اجباری نماهای ظاهری ارائه دهید، آسان تر می کند. این اصطلاح نیاز به توضیح دارد، اما مفاهیم برای طراحی اپلیکیشن شما مهم هستند.
پارادایم برنامه نویسی اعلانی
از لحاظ تاریخی، سلسله مراتب نمای اندروید به عنوان درختی از ویجتهای UI قابل نمایش بوده است. از آنجایی که وضعیت برنامه به دلیل مواردی مانند تعاملات کاربر تغییر می کند، سلسله مراتب رابط کاربری باید برای نمایش داده های فعلی به روز شود. رایج ترین راه برای به روز رسانی رابط کاربری این است که با استفاده از توابعی مانند findViewById()
روی درخت راه بروید و با فراخوانی روش هایی مانند button.setText(String)
، container.addChild(View)
یا img.setImageBitmap(Bitmap)
گره ها را تغییر دهید. این روش ها وضعیت داخلی ویجت را تغییر می دهند.
دستکاری نماها به صورت دستی احتمال خطا را افزایش می دهد. اگر یک قطعه داده در چندین مکان ارائه شود، به راحتی فراموش می شود که یکی از نماهایی را که آن را نشان می دهد به روز کنید. ایجاد حالت های غیرقانونی نیز آسان است، زمانی که دو به روز رسانی به شیوه ای غیرمنتظره با هم تضاد داشته باشند. برای مثال، ممکن است یک بهروزرسانی سعی کند مقدار گرهای را که به تازگی از رابط کاربری حذف شده است، تنظیم کند. به طور کلی، پیچیدگی نگهداری نرم افزار با تعداد بازدیدهایی که نیاز به به روز رسانی دارند، افزایش می یابد.
در طی چندین سال گذشته، کل صنعت شروع به تغییر به سمت یک مدل UI اعلامی کرده است، که مهندسی مرتبط با ساخت و بهروزرسانی رابطهای کاربری را بسیار ساده میکند. این تکنیک با بازسازی مفهومی کل صفحه نمایش از ابتدا و سپس اعمال تنها تغییرات لازم کار می کند. این رویکرد از پیچیدگی بهروزرسانی دستی سلسله مراتب نمای حالتی جلوگیری میکند. Compose یک چارچوب رابط کاربری اعلامی است.
یک چالش با بازسازی کل صفحه نمایش این است که به طور بالقوه گران است، از نظر زمان، قدرت محاسباتی و مصرف باتری. برای کاهش این هزینه، Compose بهطور هوشمندانه انتخاب میکند که کدام بخش از UI باید در هر زمان معین دوباره ترسیم شود. همانطور که در Recomposition توضیح داده شد، این موضوع برای نحوه طراحی اجزای UI خود پیامدهایی دارد.
یک تابع قابل ترکیب ساده
با استفاده از Compose، میتوانید رابط کاربری خود را با تعریف مجموعهای از توابع قابل ترکیب که دادهها را میگیرند و عناصر UI را منتشر میکنند، بسازید. یک مثال ساده یک ویجت Greeting
است که یک String
را می گیرد و یک ویجت Text
را منتشر می کند که پیام تبریک را نمایش می دهد.
شکل 1. یک تابع ساده قابل ترکیب که داده ها را ارسال می کند و از آن برای ارائه یک ویجت متنی روی صفحه استفاده می کند.
چند نکته قابل توجه در مورد این تابع:
تابع با حاشیه نویسی
@Composable
حاشیه نویسی شده است. همه توابع Composable باید این حاشیه نویسی را داشته باشند. این حاشیه نویسی به کامپایلر Compose اطلاع می دهد که این تابع برای تبدیل داده ها به UI در نظر گرفته شده است.تابع داده ها را می گیرد. توابع قابل ترکیب می توانند پارامترهایی را بپذیرند که به منطق برنامه اجازه می دهد تا رابط کاربری را توصیف کند. در این حالت، ویجت ما یک
String
میپذیرد تا بتواند با نام کاربر را احوالپرسی کند.این تابع متن را در رابط کاربری نمایش می دهد. این کار را با فراخوانی تابع composable
Text()
انجام می دهد که در واقع عنصر UI متن را ایجاد می کند. توابع Composable سلسله مراتب UI را با فراخوانی سایر توابع قابل ترکیب منتشر می کنند.تابع چیزی را بر نمی گرداند. توابعی که UI منتشر می کنند نیازی به برگرداندن چیزی ندارند، زیرا آنها به جای ساختن ویجت های UI وضعیت صفحه مورد نظر را توصیف می کنند.
این عملکرد سریع، ناتوان و بدون عوارض جانبی است.
- عملکرد زمانی که چندین بار با یک آرگومان فراخوانی می شود یکسان عمل می کند و از مقادیر دیگری مانند متغیرهای سراسری یا فراخوانی
random()
استفاده نمی کند. - این تابع، UI را بدون هیچ گونه عوارض جانبی، مانند تغییر ویژگی ها یا متغیرهای سراسری، توصیف می کند.
به طور کلی، به دلایلی که در Recomposition بحث شد، همه توابع قابل ترکیب باید با این خصوصیات نوشته شوند.
- عملکرد زمانی که چندین بار با یک آرگومان فراخوانی می شود یکسان عمل می کند و از مقادیر دیگری مانند متغیرهای سراسری یا فراخوانی
تغییر پارادایم اعلامی
با بسیاری از ابزارهای ضروری UI شی گرا، شما UI را با نمونه سازی درختی از ویجت ها مقداردهی اولیه می کنید. شما اغلب این کار را با باد کردن یک فایل طرح بندی XML انجام می دهید. هر ویجت حالت داخلی خود را حفظ می کند و روش های گیرنده و تنظیم کننده را نشان می دهد که به منطق برنامه اجازه می دهد با ویجت تعامل داشته باشد.
در رویکرد اعلانی Compose، ویجت ها نسبتاً بدون حالت هستند و توابع تنظیم کننده یا دریافت کننده را نشان نمی دهند. در واقع، ویجت ها به عنوان اشیا در معرض دید قرار نمی گیرند. شما UI را با فراخوانی یک تابع قابل ترکیب با آرگومان های مختلف به روز می کنید. این امر ارائه حالت به الگوهای معماری مانند ViewModel
را آسان می کند، همانطور که در راهنمای معماری برنامه توضیح داده شده است. سپس، اجزای سازنده شما هر بار که دادههای قابل مشاهده بهروزرسانی میشوند، مسئول تبدیل وضعیت فعلی برنامه به یک رابط کاربری هستند.
شکل 2. منطق برنامه داده ها را به تابع ترکیبی سطح بالا ارائه می دهد. این تابع از دادهها برای توصیف رابط کاربری با فراخوانی سایر اجزای سازنده استفاده میکند و دادههای مناسب را به آن composableها و در پایین سلسله مراتب ارسال میکند.
هنگامی که کاربر با رابط کاربری تعامل می کند، رابط کاربری رویدادهایی مانند onClick
را افزایش می دهد. این رویدادها باید منطق برنامه را مطلع کنند، که سپس می تواند وضعیت برنامه را تغییر دهد. هنگامی که وضعیت تغییر می کند، توابع قابل ترکیب مجدد با داده های جدید فراخوانی می شوند. این باعث می شود که عناصر UI دوباره ترسیم شوند - این فرآیند ترکیب مجدد نامیده می شود.
شکل 3. کاربر با یک عنصر UI تعامل کرد و باعث ایجاد یک رویداد شد. منطق برنامه به رویداد پاسخ می دهد، سپس توابع قابل ترکیب به طور خودکار دوباره با پارامترهای جدید، در صورت لزوم، فراخوانی می شوند.
محتوای پویا
از آنجایی که توابع قابل ترکیب به جای XML در Kotlin نوشته می شوند، می توانند مانند هر کد دیگری پویا باشند. به عنوان مثال، فرض کنید می خواهید یک UI بسازید که به لیستی از کاربران خوش آمد بگوید:
@Composable fun Greeting(names: List<String>) { for (name in names) { Text("Hello $name") } }
این تابع لیستی از نام ها را می گیرد و برای هر کاربر یک تبریک ایجاد می کند. توابع قابل ترکیب می توانند بسیار پیچیده باشند. میتوانید از دستور if
استفاده کنید تا تصمیم بگیرید که آیا میخواهید عنصر UI خاصی را نشان دهید. می توانید از حلقه ها استفاده کنید. می توانید توابع کمکی را فراخوانی کنید. شما انعطاف پذیری کامل زبان اصلی را دارید. این قدرت و انعطافپذیری یکی از مزایای کلیدی Jetpack Compose است.
ترکیب مجدد
در یک مدل UI ضروری، برای تغییر یک ویجت، یک تنظیم کننده روی ویجت تماس می گیرید تا حالت داخلی آن را تغییر دهد. در Compose، تابع composable را دوباره با داده های جدید فراخوانی می کنید. انجام این کار باعث می شود که تابع دوباره ترکیب شود -- ویجت های ساطع شده توسط تابع در صورت لزوم با داده های جدید دوباره ترسیم می شوند. فریم ورک Compose می تواند به طور هوشمندانه تنها اجزایی را که تغییر کرده اند دوباره ترکیب کند.
به عنوان مثال، این تابع قابل ترکیب را در نظر بگیرید که یک دکمه را نمایش می دهد:
@Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }
هر بار که روی دکمه کلیک می شود، تماس گیرنده مقدار clicks
را به روز می کند. Compose دوباره لامبدا را با تابع Text
فراخوانی می کند تا مقدار جدید را نشان دهد. این فرآیند را ترکیب مجدد می نامند. سایر توابع که به مقدار بستگی ندارند دوباره ترکیب نمی شوند.
همانطور که بحث کردیم، ترکیب مجدد کل درخت رابط کاربری می تواند از نظر محاسباتی گران باشد، که از توان محاسباتی و عمر باتری استفاده می کند. Compose این مشکل را با این ترکیب مجدد هوشمند حل می کند.
بازترکیب فرآیند فراخوانی مجدد توابع ترکیبپذیر شما هنگام تغییر ورودیها است. این زمانی اتفاق می افتد که ورودی های تابع تغییر کند. وقتی Compose بر اساس ورودیهای جدید دوباره ترکیب میکند، فقط توابع یا لامبداهایی را که ممکن است تغییر کرده باشند فراخوانی میکند و بقیه را رد میکند. با نادیده گرفتن همه توابع یا لامبداهایی که پارامترهای تغییر یافته را ندارند، Compose میتواند بهطور مؤثری ترکیببندی مجدد کند.
هرگز به عوارض جانبی اجرای توابع قابل ترکیب وابسته نباشید، زیرا ممکن است از ترکیب مجدد یک تابع صرفنظر شود. اگر چنین کنید، کاربران ممکن است رفتارهای عجیب و غیرقابل پیش بینی را در برنامه شما تجربه کنند. یک اثر جانبی هر تغییری است که برای بقیه برنامه شما قابل مشاهده است. به عنوان مثال، این اقدامات همه عوارض جانبی خطرناکی هستند:
- نوشتن به یک ویژگی یک شی مشترک
- به روز رسانی یک قابل مشاهده در
ViewModel
- به روز رسانی تنظیمات برگزیده مشترک
توابع قابل ترکیب ممکن است به اندازه هر فریم، مانند زمانی که یک انیمیشن در حال ارائه است، دوباره اجرا شوند. توابع قابل ترکیب باید سریع باشند تا در طول انیمیشنها از jank جلوگیری شود. اگر نیاز به انجام عملیات گران قیمت دارید، مانند خواندن از ترجیحات مشترک، آن را در یک پسزمینه انجام دهید و نتیجه مقدار را بهعنوان پارامتر به تابع composable ارسال کنید.
به عنوان مثال، این کد یک composable برای به روز رسانی یک مقدار در SharedPreferences
ایجاد می کند. سازنده نباید از روی ترجیحات مشترک بخواند یا بنویسد. در عوض، این کد خواندن و نوشتن را به یک ViewModel
در یک برنامه پسزمینه منتقل میکند. منطق برنامه مقدار فعلی را با یک تماس برگشتی برای راه اندازی به روز رسانی ارسال می کند.
@Composable fun SharedPrefsToggle( text: String, value: Boolean, onValueChanged: (Boolean) -> Unit ) { Row { Text(text) Checkbox(checked = value, onCheckedChange = onValueChanged) } }
این سند تعدادی از مواردی را که باید هنگام استفاده از Compose از آنها آگاه بود را مورد بحث قرار می دهد:
- بازترکیب تا آنجا که ممکن است بسیاری از توابع و لامبداهای قابل ترکیب را نادیده می گیرد.
- ترکیب مجدد خوشبینانه است و ممکن است لغو شود.
- یک تابع قابل ترکیب ممکن است به دفعات به اندازه هر فریم از یک انیمیشن اجرا شود.
- توابع قابل ترکیب می توانند به صورت موازی اجرا شوند.
- توابع قابل ترکیب می توانند به هر ترتیبی اجرا شوند.
بخشهای زیر نحوه ساخت توابع قابل ترکیب برای پشتیبانی از ترکیب مجدد را پوشش میدهند. در هر مورد، بهترین روش این است که توابع قابل ترکیب خود را سریع، بی قدرت و بدون عوارض جانبی نگه دارید.
ترکیب مجدد تا آنجا که ممکن است حذف می شود
وقتی بخشهایی از UI شما نامعتبر است، Compose تمام تلاش خود را میکند تا فقط بخشهایی را که باید بهروزرسانی شوند، دوباره ترکیب کند. این بدان معنی است که ممکن است از اجرای مجدد یک Button's composable بدون اجرای هیچ یک از composable های بالا یا پایین آن در درخت UI صرفنظر کند.
هر تابع قابل ترکیب و لامبدا ممکن است به خودی خود دوباره ترکیب شود. در اینجا یک مثال آورده شده است که نشان می دهد چگونه ترکیب مجدد می تواند برخی از عناصر را هنگام ارائه یک لیست نادیده بگیرد:
/** * Display a list of names the user can click with a header */ @Composable fun NamePicker( header: String, names: List<String>, onNameClicked: (String) -> Unit ) { Column { // this will recompose when [header] changes, but not when [names] changes Text(header, style = MaterialTheme.typography.bodyLarge) HorizontalDivider() // LazyColumn is the Compose version of a RecyclerView. // The lambda passed to items() is similar to a RecyclerView.ViewHolder. LazyColumn { items(names) { name -> // When an item's [name] updates, the adapter for that item // will recompose. This will not recompose when [header] changes NamePickerItem(name, onNameClicked) } } } } /** * Display a single name the user can click. */ @Composable private fun NamePickerItem(name: String, onClicked: (String) -> Unit) { Text(name, Modifier.clickable(onClick = { onClicked(name) })) }
هر یک از این دامنه ها ممکن است تنها چیزی باشد که در طول یک ترکیب مجدد اجرا می شود. هنگام تغییر header
ممکن است نوشتن بدون اجرای هیچ یک از والدین آن به Column
لامبدا رد شود. و هنگام اجرای Column
، Compose ممکن است انتخاب کند که در صورت عدم تغییر names
، از موارد LazyColumn
رد شود.
باز هم، اجرای همه توابع قابل ترکیب یا لامبدا باید بدون عارضه باشد. هنگامی که نیاز به انجام یک عارضه جانبی دارید، آن را از پاسخ به تماس فعال کنید.
ترکیب مجدد خوش بینانه است
هر زمان که Compose فکر کند که پارامترهای یک composable ممکن است تغییر کرده باشد، recomposition شروع می شود. ترکیب مجدد خوشبینانه است، به این معنی که Compose انتظار دارد قبل از تغییر مجدد پارامترها، ترکیب مجدد را به پایان برساند. اگر پارامتری قبل از پایان ترکیب مجدد تغییر کند ، Compose ممکن است ترکیب مجدد را لغو کند و با پارامتر جدید مجدداً راه اندازی شود.
هنگامی که ترکیب مجدد لغو می شود، Compose درخت رابط کاربری را از ترکیب مجدد حذف می کند. اگر عوارض جانبی دارید که بستگی به رابط کاربری نمایش داده شده دارد، حتی اگر ترکیب لغو شود، این اثر جانبی اعمال خواهد شد. این می تواند به وضعیت برنامه ناسازگار منجر شود.
اطمینان حاصل کنید که همه توابع و لامبداهای قابل ترکیب فاقد قدرت و بدون عوارض جانبی برای مدیریت مجدد خوشبینانه هستند.
توابع قابل ترکیب ممکن است اغلب اجرا شوند
در برخی موارد، یک تابع قابل ترکیب ممکن است برای هر فریم از یک انیمیشن UI اجرا شود. اگر عملکرد عملیات گران قیمتی را انجام دهد، مانند خواندن از حافظه دستگاه، عملکرد می تواند باعث jank UI شود.
به عنوان مثال، اگر ویجت شما سعی کند تنظیمات دستگاه را بخواند، به طور بالقوه می تواند آن تنظیمات را صدها بار در ثانیه بخواند و اثرات مخربی بر عملکرد برنامه شما داشته باشد.
اگر تابع composable شما به داده نیاز دارد، باید پارامترهایی را برای داده ها تعریف کند. سپس می توانید کارهای گران قیمت را به رشته دیگری خارج از ترکیب بندی منتقل کنید و داده ها را با استفاده از mutableStateOf
یا LiveData
به Compose ارسال کنید.
توابع قابل ترکیب می توانند به صورت موازی اجرا شوند
Compose میتواند با اجرای موازی توابع قابل ترکیب، ترکیب مجدد را بهینه کند. این به Compose اجازه میدهد از چندین هسته استفاده کند و عملکردهای قابل ترکیب را بدون اولویت روی صفحه اجرا کند.
این بهینه سازی به این معنی است که یک تابع ترکیبی ممکن است در مجموعه ای از رشته های پس زمینه اجرا شود. اگر یک تابع composable تابعی را در ViewModel
فراخوانی کند، Compose ممکن است آن تابع را از چند رشته به طور همزمان فراخوانی کند.
برای اطمینان از اینکه برنامه شما به درستی رفتار می کند، همه توابع قابل ترکیب نباید هیچ گونه عوارض جانبی داشته باشند. درعوض، عوارض جانبی تماسهای برگشتی مانند onClick
را که همیشه در رشته رابط کاربری اجرا میشوند، ایجاد کنید.
هنگامی که یک تابع ترکیبی فراخوانی می شود، فراخوانی ممکن است در رشته ای متفاوت از تماس گیرنده رخ دهد. این بدان معناست که از کدهایی که متغیرها را در یک لامبدای ترکیبپذیر تغییر میدهند باید اجتناب شود – هم به این دلیل که چنین کدی امن نیست و هم به این دلیل که یک اثر جانبی غیرمجاز لامبدای ترکیبپذیر است.
در اینجا یک مثال نشان می دهد که یک composable را نشان می دهد که یک لیست و تعداد آن را نمایش می دهد:
@Composable fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } }
این کد بدون عوارض جانبی است و لیست ورودی را به UI تبدیل می کند. این کد عالی برای نمایش یک لیست کوچک است. با این حال، اگر تابع روی یک متغیر محلی بنویسد، این کد از نظر موضوعی امن یا صحیح نخواهد بود:
@Composable fun ListWithBug(myList: List<String>) { var items = 0 Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Card { Text("Item: $item") items++ // Avoid! Side-effect of the column recomposing. } } } Text("Count: $items") } }
در این مثال، items
با هر ترکیب مجدد اصلاح می شوند. این می تواند هر فریم از یک انیمیشن یا زمانی که لیست به روز می شود. در هر صورت، UI تعداد اشتباه را نمایش می دهد. به همین دلیل، نوشته هایی مانند این در Compose پشتیبانی نمی شوند. با ممنوع کردن این نوشتهها، به چارچوب اجازه میدهیم تا رشتهها را برای اجرای لامبداهای قابل ترکیب تغییر دهد.
توابع قابل ترکیب می توانند به هر ترتیبی اجرا شوند
اگر به کد یک تابع قابل ترکیب نگاه کنید، ممکن است فرض کنید که کد به ترتیب ظاهر شده اجرا می شود. اما این تضمینی برای صحت ندارد. اگر یک تابع قابل ترکیب شامل فراخوانیهای دیگر توابع قابل ترکیب باشد، آن توابع ممکن است به هر ترتیبی اجرا شوند. Compose این امکان را دارد که تشخیص دهد برخی از عناصر UI اولویت بیشتری نسبت به سایرین دارند و ابتدا آنها را ترسیم کند.
به عنوان مثال، فرض کنید کدی مانند این برای ترسیم سه صفحه در طرح بندی برگه دارید:
@Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }
تماسها با StartScreen
، MiddleScreen
و EndScreen
ممکن است به هر ترتیبی انجام شوند. این به این معنی است که شما نمیتوانید، برای مثال، StartScreen()
یک متغیر سراسری (عوارض جانبی) تنظیم کنید و MiddleScreen()
از آن تغییر استفاده کند. درعوض، هر یک از این توابع باید خودکفا باشند.
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد نحوه تفکر در توابع Compose و composable، منابع اضافی زیر را بررسی کنید.
ویدیوها
{% کلمه به کلمه %}برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- Kotlin برای Jetpack Compose
- State و Jetpack Compose
- لایه بندی معماری Jetpack Compose
Jetpack Compose یک جعبه ابزار UI مدرن برای اندروید است. نوشتن، نوشتن و حفظ رابط کاربری برنامه شما را با ارائه یک API اعلامی که به شما امکان می دهد رابط کاربری برنامه خود را بدون تغییر اجباری نماهای ظاهری ارائه دهید، آسان تر می کند. این اصطلاح نیاز به توضیح دارد، اما مفاهیم برای طراحی اپلیکیشن شما مهم هستند.
پارادایم برنامه نویسی اعلانی
از لحاظ تاریخی، سلسله مراتب نمای اندروید به عنوان درختی از ویجتهای UI قابل نمایش بوده است. از آنجایی که وضعیت برنامه به دلیل مواردی مانند تعاملات کاربر تغییر می کند، سلسله مراتب رابط کاربری باید برای نمایش داده های فعلی به روز شود. رایج ترین راه برای به روز رسانی رابط کاربری این است که با استفاده از توابعی مانند findViewById()
روی درخت راه بروید و با فراخوانی روش هایی مانند button.setText(String)
، container.addChild(View)
یا img.setImageBitmap(Bitmap)
گره ها را تغییر دهید. این روش ها وضعیت داخلی ویجت را تغییر می دهند.
دستکاری نماها به صورت دستی احتمال خطا را افزایش می دهد. اگر یک قطعه داده در چندین مکان ارائه شود، به راحتی فراموش می شود که یکی از نماهایی را که آن را نشان می دهد به روز کنید. ایجاد حالت های غیرقانونی نیز آسان است، زمانی که دو به روز رسانی به شیوه ای غیرمنتظره با هم تضاد داشته باشند. برای مثال، ممکن است یک بهروزرسانی سعی کند مقدار گرهای را که به تازگی از رابط کاربری حذف شده است، تنظیم کند. به طور کلی، پیچیدگی نگهداری نرم افزار با تعداد بازدیدهایی که نیاز به به روز رسانی دارند، افزایش می یابد.
در طی چندین سال گذشته، کل صنعت شروع به تغییر به سمت یک مدل UI اعلامی کرده است، که مهندسی مرتبط با ساخت و بهروزرسانی رابطهای کاربری را بسیار ساده میکند. این تکنیک با بازسازی مفهومی کل صفحه نمایش از ابتدا و سپس اعمال تنها تغییرات لازم کار می کند. این رویکرد از پیچیدگی به روزرسانی دستی یک سلسله مراتب دیدنی جلوگیری می کند. آهنگسازی یک چارچوب UI اعلامی است.
یکی از چالش های بازسازی کل صفحه نمایش این است که از نظر زمان ، محاسبات قدرت و استفاده از باتری به طور بالقوه گران است. برای کاهش این هزینه ، با هوشمندی انتخاب می کند که کدام بخش از UI باید در هر زمان معینی دوباره تغییر یابد. این امر پیامدهای زیادی برای نحوه طراحی مؤلفه های UI خود دارد ، همانطور که در بازپرداخت بحث شده است.
یک عملکرد ساده آهنگسازی
با استفاده از آهنگسازی ، می توانید رابط کاربری خود را با تعریف مجموعه ای از توابع سازنده که در داده ها گرفته شده و عناصر UI را منتشر می کنند ، بسازید. یک مثال ساده ویجت Greeting
است که یک String
را می گیرد و یک ویجت Text
را منتشر می کند که یک پیام تبریک را نشان می دهد.
شکل 1. یک تابع ساده سازنده که داده ها را منتقل می کند و از آن برای ارائه ویجت متنی روی صفحه استفاده می کند.
چند نکته قابل توجه در مورد این عملکرد:
این عملکرد با حاشیه نویسی
@Composable
حاشیه نویسی می شود. تمام کارکردهای کامپوزیت باید این حاشیه نویسی را داشته باشند. این حاشیه نویسی به کامپایلر آهنگساز اطلاع می دهد که این عملکرد برای تبدیل داده ها به UI در نظر گرفته شده است.عملکرد در داده ها می گیرد. توابع کامپوزیت می توانند پارامترهایی را بپذیرند ، که به منطق برنامه اجازه می دهد UI را توصیف کند. در این حالت ، ویجت ما
String
ای را می پذیرد تا بتواند با نام به کاربر سلام کند.عملکرد متن را در UI نشان می دهد. این کار را با فراخوانی
Text()
متن ترکیب ، که در واقع عنصر UI متن را ایجاد می کند ، این کار را انجام می دهد. توابع کامپوزیت با فراخوانی سایر توابع سازنده ، سلسله مراتب UI را منتشر می کنند.عملکرد هیچ چیزی را برمی گرداند. کارکردهایی را تشکیل می دهد که UI را منتشر می کند ، نیازی به بازگشت به چیزی ندارد ، زیرا آنها به جای ساخت ابزارک های UI ، وضعیت صفحه مورد نظر را توصیف می کنند.
این عملکرد سریع ، idempotent و عاری از عوارض جانبی است.
- این عملکرد وقتی چندین بار با همان استدلال خوانده می شود ، به همان روش رفتار می کند و از مقادیر دیگری مانند متغیرهای جهانی یا تماس با
random()
. - این تابع UI را بدون هیچ گونه عوارض جانبی ، مانند اصلاح خصوصیات یا متغیرهای جهانی توصیف می کند.
به طور کلی ، تمام کارکردهای کامپوزیت باید به دلایلی که در بازپرداخت مورد بحث قرار گرفته است ، با این خصوصیات نوشته شوند.
- این عملکرد وقتی چندین بار با همان استدلال خوانده می شود ، به همان روش رفتار می کند و از مقادیر دیگری مانند متغیرهای جهانی یا تماس با
تغییر پارادایم اعلامیه
با بسیاری از ابزارهای ضروری UI شی گرا ، شما UI را با فوری درخت ابزارک ها آغاز می کنید. شما اغلب این کار را با تورم یک فایل طرح XML انجام می دهید. هر ویجت وضعیت داخلی خود را حفظ می کند و روشهای گیرنده و تنظیم کننده را در معرض دید قرار می دهد که به منطق برنامه اجازه می دهد تا با ویجت تعامل داشته باشد.
در رویکرد اعلامی کامپوز ، ابزارک ها نسبتاً بدون تابعیت هستند و عملکردهای تنظیم کننده یا گیرنده را در معرض نمایش قرار نمی دهند. در حقیقت ، ابزارک ها به عنوان اشیاء در معرض دید قرار نمی گیرند. شما UI را با فراخوانی همان عملکرد ترکیب یکسان با آرگومان های مختلف به روز می کنید. این امر باعث می شود که دولت به الگوهای معماری مانند ViewModel
، همانطور که در راهنمای معماری برنامه شرح داده شده است ، آسان شود. سپس ، ترکیبات شما وظیفه دارند هر بار که به روزرسانی داده های قابل مشاهده را به UI تبدیل می کنند.
شکل 2. منطق برنامه داده ها را به عملکرد ترکیب سطح بالا ارائه می دهد. این تابع از داده ها برای توصیف UI با فراخوانی سایر آهنگسازی ها استفاده می کند و داده های مناسب را به آن آهنگسازان و در پایین سلسله مراتب منتقل می کند.
هنگامی که کاربر با UI در تعامل است ، UI رویدادهایی مانند onClick
را ایجاد می کند. این رویدادها باید منطق برنامه را اعلام کنند ، که می تواند وضعیت برنامه را تغییر دهد. هنگامی که حالت تغییر می کند ، توابع سازنده مجدداً با داده های جدید فراخوانی می شوند. این امر باعث می شود عناصر UI مجدداً مورد استفاده قرار گیرند-این فرآیند به عنوان بازپرداخت نامیده می شود.
شکل 3 کاربر با یک عنصر UI در تعامل است و باعث ایجاد یک رویداد می شود. منطق برنامه به این رویداد پاسخ می دهد ، سپس توابع سازنده در صورت لزوم دوباره به طور خودکار با پارامترهای جدید فراخوانی می شوند.
محتوای پویا
از آنجا که توابع کامپوزیت به جای XML در Kotlin نوشته شده است ، می توانند به اندازه سایر کد Kotlin پویا باشند. به عنوان مثال ، فرض کنید می خواهید UI بسازید که به لیستی از کاربران سلام می کند:
@Composable fun Greeting(names: List<String>) { for (name in names) { Text("Hello $name") } }
این عملکرد لیستی از نام ها را به خود اختصاص داده و برای هر کاربر سلام و احوالپرسی می کند. توابع کامپوزیت می توانند کاملاً پیشرفته باشند. اگر می خواهید یک عنصر UI خاص را نشان دهید ، می توانید if
بیانیه استفاده کنید. می توانید از حلقه ها استفاده کنید. می توانید با توابع یاور تماس بگیرید. شما انعطاف پذیری کامل زبان اساسی را دارید. این قدرت و انعطاف پذیری یکی از مهمترین مزایای آهنگسازی Jetpack است.
ترکیب مجدد
در یک مدل UI ضروری ، برای تغییر ویجت ، شما یک تنظیم کننده را روی ویجت صدا می کنید تا وضعیت داخلی آن را تغییر دهید. در آهنگسازی ، شما دوباره عملکرد ترکیب را با داده های جدید فراخوانی می کنید. انجام این کار باعث می شود که عملکرد مجدداً مورد استفاده قرار گیرد -ویجت های ساطع شده توسط عملکرد ، در صورت لزوم ، با داده های جدید دوباره ساخته می شوند. فریم ورک Compose می تواند به طور هوشمندانه تنها اجزایی را که تغییر کرده اند دوباره ترکیب کند.
به عنوان مثال ، این تابع ترکیب را در نظر بگیرید که یک دکمه را نشان می دهد:
@Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }
هر بار که دکمه کلیک می شود ، تماس گیرنده مقدار clicks
به روز می کند. آهنگسازی را دوباره با تابع Text
تماس می گیرد تا مقدار جدید را نشان دهد. این فرآیند با استفاده از این روند نامیده می شود. توابع دیگری که به مقدار بستگی ندارند ، مجدداً مورد استفاده قرار نمی گیرند.
همانطور که بحث کردیم ، بازآفرینی کل درخت UI می تواند از نظر محاسباتی گران باشد ، که از محاسبات و باتری محاسبات استفاده می کند. آهنگسازی این مشکل را با این بازپرداخت هوشمند حل می کند.
بازپرداخت فرآیند فراخوانی توابع ترکیب شما در هنگام تغییر ورودی ها است. این اتفاق می افتد که ورودی های عملکرد تغییر کنند. هنگامی که آهنگسازی را بر اساس ورودی های جدید ارائه می دهد ، فقط توابع یا لامبدهایی را که ممکن است تغییر کرده باشد فراخوانی می کند و بقیه را رد می کند. با پرش از تمام توابع یا بره های که پارامترهای تغییر نکرده اند ، آهنگسازی می تواند به طور مؤثر دوباره انجام شود.
هرگز از اجرای توابع کامپوزیت به عوارض جانبی وابسته نیستید ، زیرا ممکن است بازپرداخت عملکرد از بین برود. اگر این کار را انجام دهید ، کاربران ممکن است در برنامه شما رفتار عجیب و غیرقابل پیش بینی را تجربه کنند. یک اثر جانبی هر تغییری است که برای بقیه برنامه شما قابل مشاهده است. به عنوان مثال ، این اقدامات همه عوارض جانبی خطرناک است:
- نوشتن به یک ویژگی یک شیء مشترک
- به روزرسانی قابل مشاهده در
ViewModel
- به روزرسانی ترجیحات مشترک
توابع کامپوزیت ممکن است مانند هر قاب ، مانند زمان ارائه یک انیمیشن ، مجدداً اجرا شوند. توابع کامپوزیت باید سریع باشد تا در طول انیمیشن از جنجال جلوگیری شود. اگر شما نیاز به انجام عملیات گران قیمت مانند خواندن از ترجیحات مشترک دارید ، این کار را در یک پس زمینه انجام دهید و نتیجه مقدار را به عنوان یک پارامتر به عملکرد کامپوزیت منتقل کنید.
به عنوان نمونه ، این کد برای به روزرسانی یک مقدار در SharedPreferences
یک ترکیب را ایجاد می کند. این ترکیب نباید از ترجیحات مشترک خود بخواند یا بنویسد. در عوض ، این کد خواندن و نوشتن را به یک ViewModel
در یک Coroutine پس زمینه منتقل می کند. منطق برنامه مقدار فعلی را با یک پاسخ به تماس منتقل می کند تا به روزرسانی شود.
@Composable fun SharedPrefsToggle( text: String, value: Boolean, onValueChanged: (Boolean) -> Unit ) { Row { Text(text) Checkbox(checked = value, onCheckedChange = onValueChanged) } }
این سند در مورد تعدادی از مواردی که باید هنگام استفاده از آهنگسازی از آنها آگاه باشید ، بحث می کند:
- بازپرداخت هرچه بیشتر توابع سازنده و لامبدا را می پرید.
- بازپرداخت خوش بین است و ممکن است لغو شود.
- یک تابع ترکیب ممکن است به طور مکرر اجرا شود ، مانند هر قاب یک انیمیشن.
- توابع کامپوزیت می توانند به طور موازی اجرا شوند.
- توابع کامپوزیت می توانند به هر ترتیب اجرا شوند.
در بخش های زیر نحوه ساخت توابع کامپوزیت برای پشتیبانی از بازپرداخت ارائه می شود. در هر صورت ، بهترین روش این است که عملکردهای ترکیب خود را سریع ، idempotent و عوارض جانبی خود نگه دارید.
بازپرداخت تا حد امکان رد می شود
هنگامی که بخش هایی از UI شما نامعتبر است ، آهنگسازی تمام تلاش خود را می کند تا فقط بخش هایی را که باید به روز شوند ، دوباره انجام دهند. این بدان معناست که ممکن است بدون اجرای هر یک از ترکیبات در بالا یا زیر آن در درخت UI ، مجدداً آهنگسازی یک دکمه را دوباره اجرا کند.
هر عملکردی و لامبدا ممکن است به خودی خود دوباره تکرار شود. در اینجا مثالی آورده شده است که نشان می دهد چگونه می توان هنگام ارائه لیست ، برخی از عناصر را رد کرد:
/** * Display a list of names the user can click with a header */ @Composable fun NamePicker( header: String, names: List<String>, onNameClicked: (String) -> Unit ) { Column { // this will recompose when [header] changes, but not when [names] changes Text(header, style = MaterialTheme.typography.bodyLarge) HorizontalDivider() // LazyColumn is the Compose version of a RecyclerView. // The lambda passed to items() is similar to a RecyclerView.ViewHolder. LazyColumn { items(names) { name -> // When an item's [name] updates, the adapter for that item // will recompose. This will not recompose when [header] changes NamePickerItem(name, onNameClicked) } } } } /** * Display a single name the user can click. */ @Composable private fun NamePickerItem(name: String, onClicked: (String) -> Unit) { Text(name, Modifier.clickable(onClick = { onClicked(name) })) }
هر یک از این دامنه ها ممکن است تنها چیزی باشد که در طی یک بازپرداخت اجرا می شود. آهنگسازی ممکن است بدون اجرای هیچ یک از والدین خود در هنگام تغییر header
، به Column
Lambda بپردازد. و در هنگام اجرای Column
، اگر names
تغییر نکرد ، می تواند موارد LazyColumn
را پرش کند.
باز هم ، اجرای کلیه توابع سازنده یا لامبدا باید بدون اثر جانبی باشد. هنگامی که شما نیاز به انجام یک اثر جانبی دارید ، آن را از پاسخ به تماس بگیرید.
تجدید نظر خوش بین است
بازپرداخت هر زمان که آهنگسازی فکر می کند که پارامترهای یک ترکیب ممکن است تغییر کرده باشد ، شروع می شود. بازپرداخت خوش بین است ، به این معنی که آهنگسازی انتظار دارد قبل از تغییر پارامترها دوباره ، بازپرداخت را تمام کند. اگر یک پارامتر قبل از اتمام مجدد تغییر کند ، آهنگسازی ممکن است مجدداً بازپرداخت را لغو کرده و آن را با پارامتر جدید مجدداً راه اندازی کند.
هنگامی که بازپرداخت لغو می شود ، ترکیب درخت UI را از بازپرداخت دور می کند. اگر عوارض جانبی داشته باشید که به UI نمایش داده می شود ، از عوارض جانبی حتی در صورت لغو ترکیب استفاده می شود. این می تواند به حالت برنامه متناقض منجر شود.
اطمینان حاصل کنید که تمام عملکردهای کامپوزیت و لامبدها برای انجام تجدیدنظر خوش بینانه و عاری از عارضه جانبی هستند.
توابع کامپوزیت ممکن است به طور مکرر اجرا شود
در بعضی موارد ، یک تابع ترکیب ممکن است برای هر فریم از یک انیمیشن UI اجرا شود. اگر این عملکرد عملیات گران قیمت مانند خواندن از ذخیره سازی دستگاه را انجام دهد ، این عملکرد می تواند باعث Jank UI شود.
به عنوان مثال ، اگر ویجت شما سعی در خواندن تنظیمات دستگاه داشت ، می تواند به طور بالقوه آن تنظیمات را صدها بار در ثانیه بخواند ، با تأثیرات فاجعه بار بر عملکرد برنامه شما.
اگر عملکرد کامپوزیت شما به داده نیاز دارد ، باید پارامترهایی را برای داده ها تعریف کند. سپس می توانید کار گران قیمت را به یک موضوع دیگر ، خارج از ترکیب منتقل کنید و داده ها را برای تهیه با استفاده از mutableStateOf
یا LiveData
منتقل کنید.
توابع کامپوزیت می توانند به صورت موازی اجرا شوند
آهنگسازی می تواند با اجرای توابع کامپوزیت به طور موازی ، بازآفرینی را بهینه کند. این امر باعث می شود که آهنگسازی از چندین هسته استفاده کند و توابع کامپوزیت را با اولویت پایین تر روی صفحه اجرا کند.
این بهینه سازی به این معنی است که یک تابع ترکیب ممکن است در استخر موضوعات پس زمینه اجرا شود. اگر یک تابع کامپوزیت یک تابع را بر روی یک ViewModel
فراخوانی کند ، آهنگسازی ممکن است به طور همزمان از چندین موضوع استفاده کند.
برای اطمینان از رفتار صحیح برنامه شما ، تمام عملکردهای کامپوزیت نباید عوارض جانبی داشته باشند. درعوض ، عوارض جانبی را از تماس تلفنی مانند onClick
که همیشه روی موضوع UI اجرا می شود ، ایجاد کنید.
هنگامی که یک تابع کامپوزیت فراخوانی می شود ، دعوت نامه ممکن است روی یک موضوع متفاوت از تماس گیرنده رخ دهد. این بدان معناست که کدی که متغیرها را در یک لامبدا ترکیب می کند ، باید از آن جلوگیری شود-هر دو زیرا چنین کدی از امنیت موضوع نیست ، و به دلیل این که یک اثر جانبی غیرقابل نفوذ از لامبدا قابل ترکیب است.
در اینجا مثالی نشان می دهد که یک ترکیب را نشان می دهد که یک لیست و تعداد آن را نشان می دهد:
@Composable fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } }
این کد بدون اثر جانبی است و لیست ورودی را به UI تبدیل می کند. این کد عالی برای نمایش یک لیست کوچک است. با این حال ، اگر این تابع به یک متغیر محلی می نویسد ، این کد ایمن یا صحیح نخواهد بود:
@Composable fun ListWithBug(myList: List<String>) { var items = 0 Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Card { Text("Item: $item") items++ // Avoid! Side-effect of the column recomposing. } } } Text("Count: $items") } }
در این مثال ، items
با هر بازپرداخت اصلاح می شوند. این می تواند هر قاب یک انیمیشن باشد ، یا وقتی لیست به روز می شود. در هر صورت ، UI تعداد اشتباه را نشان می دهد. به همین دلیل ، نوشتن مانند این در آهنگسازی پشتیبانی نمی شود. با ممنوعیت این نوشتن ها ، ما به چارچوب اجازه می دهیم تا موضوعات را برای اجرای لامبدهای کامپوزیت تغییر دهد.
توابع کامپوزیت می توانند به هر ترتیب اجرا شوند
اگر به کد یک تابع ترکیب نگاه کنید ، ممکن است فرض کنید که کد به ترتیب ظاهر می شود. اما این تضمین نمی شود که درست باشد. اگر یک تابع کامپوزیت حاوی تماس با سایر توابع سازنده باشد ، ممکن است این توابع به هر ترتیب اجرا شوند. آهنگسازی این گزینه را دارد که تشخیص دهد برخی از عناصر UI از اولویت بالاتر از سایرین هستند و ابتدا آنها را ترسیم می کنند.
به عنوان مثال ، فرض کنید شما کدی مانند این دارید تا سه صفحه را در یک طرح برگه بکشید:
@Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }
تماس های مربوط به StartScreen
، MiddleScreen
و EndScreen
ممکن است به هر ترتیب اتفاق بیفتد. این بدان معنی است که شما نمی توانید به عنوان مثال ، StartScreen()
برخی از متغیرهای جهانی (یک اثر جانبی) را تنظیم کنید و دارای MiddleScreen()
از این تغییر استفاده کنید. درعوض ، هر یک از این کارکردها باید خودمختار باشند.
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد چگونگی فکر کردن در کارکردهای آهنگسازی و ترکیب ، منابع اضافی زیر را بررسی کنید.
ویدیوها
{% کلمه به کلمه %}برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- Kotlin برای Jetpack Compose
- آهنگ و جت پک آهنگسازی می کند
- Jetpack لایه بندی معماری را تشکیل می دهد