در Compose، واسط کاربر تغییرناپذیر است—پساز رسم شدن، راهی برای بهروزرسانی آن وجود ندارد. آنچه میتوانید کنترل کنید وضعیت رابط کاربریتان است. هر بار که وضعیت
واسط کاربر تغییر میکند، Compose بخشهایی از درخت واسط کاربر را که
تغییر کردهاند بازسازی میکند. عناصر ترکیبی میتوانند وضعیت را بپذیرند و رویدادها را آشکار کنند—برای مثال،
TextField مقداری را میپذیرد و یک تابع برگشتی onValueChange را آشکار میکند که
از مدیریتکننده تابع برگشتی میخواهد مقدار را تغییر دهد.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
ازآنجاییکه عناصر ترکیبی وضعیت را میپذیرند و رویدادها را آشکار میکنند، الگوی جریان داده یکطرفه بهخوبی با Jetpack Compose سازگار است. این راهنما بر نحوه پیادهسازی الگوی جریان داده یکطرفه در Compose، نحوه پیادهسازی رویدادها و نگهدارندههای وضعیت، و نحوه کار با ViewModels در Compose تمرکز دارد.
جریان داده یکطرفه
جریان داده یکطرفه (UDF) الگوی طراحی است که در آن وضعیت به پایین جریان مییابد و رویدادها به بالا جریان مییابند. با دنبال کردن جریان داده یکطرفه، میتوانید عناصر ترکیبی را که وضعیت را در رابط کاربری نمایش میدهند از بخشهای برنامهتان که وضعیت را ذخیره و تغییر میدهند جدا کنید.
حلقه بهروزرسانی واسط کاربر برای برنامهای که از جریان داده یکطرفه استفاده میکند به این شکل است:
- رویداد: بخشی از «میانای کاربر» رویدادی تولید میکند و آن را به بالا ارسال میکند، مثلاً کلیک دکمهای که به «نمای مدل» ارسال میشود تا مدیریت شود؛ یا رویدادی از لایههای دیگر برنامه شما ارسال میشود، مثلاً نشان میدهد که جلسه کاربر منقضی شده است.
- بهروزرسانی وضعیت: مدیریتکننده رویداد ممکن است وضعیت را تغییر دهد.
- وضعیت نمایشگر: نگهدارنده وضعیت، وضعیت را به پایین منتقل میکند و واسط کاربر آن را نمایش میدهد.
پیروی از این الگو هنگام استفاده از Jetpack Compose مزایای متعددی دارد:
- آزمونپذیری: جدا کردن وضعیت از میانای کاربری که آن را نمایش میدهد، آزمون هر دو را بهصورت مجزا آسانتر میکند.
- کپسوله کردن وضعیت: ازآنجاییکه وضعیت فقط در یک مکان میتواند بهروز شود و فقط یک منبع حقیقت برای وضعیت عنصر ترکیبی وجود دارد، احتمال اینکه بهدلیل وضعیتهای ناسازگار اشکال ایجاد کنید کمتر است.
- ثبات میانای کاربر: همه بهروزرسانیهای وضعیت بااستفاده از نگهدارندههای وضعیت قابلمشاهده، مانند
StateFlowیاLiveData، بلافاصله در میانای کاربر منعکس میشوند.
جریان داده یکطرفه در Jetpack Compose
عناصر ترکیبی براساس وضعیت و رویدادها کار میکنند. برای مثال، TextField فقط زمانی بهروز میشود که پارامتر value آن بهروز شود و یک onValueChange
بازخوان—رویدادی که درخواست میکند مقدار به مقدار جدیدی تغییر کند—نمایان کند. Compose
شیء State را بهعنوان نگهدارنده مقدار تعریف میکند، و تغییرات در مقدار وضعیت
باعث بازآفرینی میشود. بسته به اینکه چه مدت باید مقدار را بهخاطر بسپارید، میتوانید وضعیت را در
remember { mutableStateOf(value) } یا
rememberSaveable { mutableStateOf(value) } نگه دارید.
نوع مقدار TextField قابل ترکیب String است، بنابراین این مقدار میتواند از هر جایی بیاید—از مقدار کدبندیشده، از «نمای مدل»، یا از قابل ترکیب والد منتقل شود. لازم نیست آن را در یک شیء State نگه دارید، اما باید
مقدار را هنگام فراخوانی onValueChange بهروز کنید.
-
mutableStateOf(value)یکMutableStateایجاد میکند که نوعی قابلمشاهده در Compose است. هرگونه تغییر در برنامه زمانی مقدار آن باعث ترکیب مجدد هریک از توابع ترکیبی میشود که آن مقدار را میخوانند. -
rememberاشیا را در ترکیب ذخیره میکند و وقتی عنصر ترکیبی کهrememberرا فراخوانده است از ترکیب برداشته میشود، شیء را فراموش میکند. rememberSaveableبا ذخیره کردن وضعیت درBundle، وضعیت را درطول تغییرات پیکربندی حفظ میکند.
تعریف پارامترهای ترکیبشدنی
هنگام تعریف پارامترهای وضعیت یک عنصر ترکیبی، سؤالات زیر را در ذهن داشته باشید:
- آیا عنصر ترکیبی قابلاستفاده مجدد یا انعطافپذیر است؟
- پارامترهای حالت چگونه بر عملکرد این عنصر ترکیبی تأثیر میگذارند؟
برای ترویج جداسازی و استفاده مجدد، هر عنصر ترکیبی باید کمترین مقدار ممکن از اطلاعات را در خود داشته باشد. برای مثال، هنگام ساختن عنصر ترکیبی برای نگهداری سرصفحه مقاله خبری، بهتر است فقط اطلاعاتی را که باید نمایش داده شود ارسال کنید، نه کل مقاله خبری:
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
گاهی اوقات، استفاده از پارامترهای فردی نیز عملکرد را بهبود میبخشد—برای مثال، اگر
News حاوی اطلاعات بیشتری نسبتبه title و subtitle باشد، هرگاه نمونه جدیدی از News به Header(news) ارسال شود،
ترکیبشونده دوباره ترکیب خواهد شد، حتی اگر title و subtitle تغییر نکرده باشند.
تعداد پارامترهایی را که ارسال میکنید بهدقت درنظر بگیرید. داشتن تابعی با پارامترهای زیاد ارگونومی تابع را کاهش میدهد، بنابراین در این مورد گروهبندی آنها در یک کلاس ترجیح داده میشود.
رویدادها در Compose
هر ورودی به برنامه شما باید بهعنوان یک رویداد نشان داده شود: ضربهها، تغییرات نوشتار،
و حتی زمانسنجها یا بهروزرسانیهای دیگر. ازآنجاییکه این رویدادها وضعیت واسط کاربر شما را تغییر میدهند،
ViewModel باید آنها را مدیریت کند و وضعیت واسط کاربر را بهروز کند.
لایه واسط کاربر نباید وضعیت را خارج از مدیریتکننده رویداد تغییر دهد زیرا این کار میتواند باعث ایجاد ناسازگاری و اشکال در برنامه شما شود.
مقادیر تغییرناپذیر را برای وضعیت و لامبداهای مدیریتکننده رویداد ترجیح دهید. این رویکرد مزایای زیر را دارد:
- قابلیت استفاده مجدد را بهبود میبخشید.
- تأیید میکنید که واسط کاربر شما مقدار وضعیت را مستقیماً تغییر نمیدهد.
- از مشکلات همزمان بودن اجتناب میکنید زیرا مطمئن میشوید که وضعیت از رشتهای دیگر تغییر نمیکند.
- اغلب، پیچیدگی کد را کاهش میدهید.
برای مثال، ترکیبی که String و لامبدا را بهعنوان پارامتر میپذیرد میتواند
از زمینههای زیادی فراخوانی شود و بسیار قابل استفاده مجدد است. فرض کنید نوار برنامه بالایی در برنامه شما همیشه نوشتار نمایش میدهد و دکمه بازگشت دارد. میتوانید MyAppTopAppBar ترکیبپذیر عمومیتری تعریف کنید که نوشتار و مدیریتکننده دکمه برگشت را بهعنوان پارامتر دریافت میکند:
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.AutoMirrored.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
ViewModels، وضعیتها، و رویدادها: مثال
بااستفاده از ViewModel و mutableStateOf، اگر یکی از موارد زیر درست باشد، میتوانید جریان داده یکطرفه را در برنامهتان معرفی کنید:
- وضعیت واسط کاربر شما بااستفاده از نگهدارندههای وضعیت قابلمشاهده، مثل
StateFlowیاLiveData، آشکار میشود. ViewModelرویدادهای دریافتی از رابط کاربری یا لایههای دیگر برنامه را مدیریت میکند و دارنده وضعیت را براساس رویدادها بهروز میکند.
برای مثال، هنگام پیادهسازی صفحه ورود به سیستم، ضربه زدن روی دکمه ورود به سیستم باید باعث شود برنامه شما گردونه پیشرفت و تماس شبکه را نمایش دهد. اگر ورود به سیستم موفقیتآمیز بود، برنامه به صفحه دیگری هدایت میشود؛ درصورت بروز خطا، برنامه «میانوعدهبار» را نشان میدهد. در اینجا نحوه مدلسازی وضعیت صفحه و رویداد آمده است:
صفحهنمایش چهار حالت دارد:
- از سیستم خارج شده است: وقتی کاربر هنوز به سیستم وارد نشده است.
- درحال انجام: وقتی برنامه شما درحال تلاش برای ورود کاربر به سیستم ازطریق انجام تماس شبکه است.
- خطا: زمانی که هنگام ورود به سیستم خطایی رخ دهد.
- وارد سیستم شده: زمانی که کاربر وارد سیستم شده است.
میتوانید این وضعیتها را بهعنوان کلاس مهرومومشده مدلسازی کنید. ViewModel وضعیت را بهعنوان State آشکار میکند، وضعیت اولیه را تنظیم میکند، و وضعیت را درصورت نیاز بهروزرسانی میکند. ViewModel همچنین رویداد ورود به سیستم را با آشکار کردن روش onSignIn() مدیریت میکند.
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
علاوهبر mutableStateOf API، Compose افزونههایی برای LiveData، Flow، و Observable ارائه میدهد تا بهعنوان شنونده ثبت شود و مقدار را بهعنوان وضعیت نشان دهد.
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
بیشتر بدانید
برای کسب اطلاعات بیشتر درباره معماری در Jetpack Compose، به منابع زیر مراجعه کنید:
نمونهها
توصیهشده برای شما
- توجه: نوشتار پیوند وقتی جاوا اسکریپت خاموش است نمایش داده میشود
- حالت و Jetpack Compose
- ذخیره وضعیت میانای کاربر در Compose
- مدیریت درونداد کاربر