حالت در برنامه هر مقداری است که می تواند در طول زمان تغییر کند. این یک تعریف بسیار گسترده است و همه چیز را از یک پایگاه داده اتاق گرفته تا یک متغیر در یک کلاس را در بر می گیرد.
همه برنامه های اندروید وضعیت را به کاربر نمایش می دهند. چند نمونه از حالت در برنامه های اندروید:
- یک نوار اسنک که نشان می دهد چه زمانی اتصال شبکه نمی تواند برقرار شود.
- یک پست وبلاگ و نظرات مرتبط.
- انیمیشنها را روی دکمههایی که وقتی کاربر روی آنها کلیک میکند پخش میشوند.
- برچسب هایی که کاربر می تواند روی یک تصویر بکشد.
Jetpack Compose به شما کمک میکند در مورد مکان و نحوه ذخیره و استفاده از وضعیت در یک برنامه Android صریح باشید. این راهنما بر ارتباط بین state و composable ها، و API هایی که Jetpack Compose برای کار راحت تر با state ارائه می دهد، تمرکز دارد.
حالت و ترکیب
Compose اعلانی است و به این ترتیب تنها راه برای به روز رسانی آن فراخوانی همان composable با آرگومان های جدید است. این آرگومان ها نمایشی از وضعیت UI هستند. هر زمان که وضعیتی به روز می شود ، ترکیب مجدد صورت می گیرد. در نتیجه، چیزهایی مانند TextField
به طور خودکار مانند نماهای ضروری مبتنی بر XML به روز نمی شوند. به یک composable باید به صراحت وضعیت جدید گفته شود تا مطابق آن به روز شود.
@Composable private fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField( value = "", onValueChange = { }, label = { Text("Name") } ) } }
اگر این را اجرا کنید و سعی کنید متن را وارد کنید، خواهید دید که هیچ اتفاقی نمی افتد. دلیلش این است که TextField
خودش را به روز نمی کند - زمانی که پارامتر value
آن تغییر می کند، به روز می شود. این به دلیل نحوه کار ترکیب و ترکیب مجدد در Compose است.
برای کسب اطلاعات بیشتر در مورد ترکیب اولیه و ترکیب مجدد، به تفکر در نوشتن مراجعه کنید.
حالت در ترکیبات
توابع Composable می توانند از remember
API برای ذخیره یک شی در حافظه استفاده کنند. مقداری که با remember
محاسبه می شود در ترکیب اولیه در Composition ذخیره می شود و مقدار ذخیره شده در طول ترکیب مجدد برگردانده می شود. remember
می تواند برای ذخیره اشیاء تغییرپذیر و غیرقابل تغییر استفاده شود.
mutableStateOf
یک MutableState<T>
قابل مشاهده ایجاد می کند که یک نوع قابل مشاهده است که با زمان اجرا ترکیب شده است.
interface MutableState<T> : State<T> {
override var value: T
}
هر گونه تغییر در value
، ترکیب مجدد هر توابع ترکیبی که value
میخواند، انجام میدهد.
سه راه برای اعلام یک شی MutableState
در یک composable وجود دارد:
-
val mutableState = remember { mutableStateOf(default) }
-
var value by remember { mutableStateOf(default) }
-
val (value, setValue) = remember { mutableStateOf(default) }
این اعلان ها معادل هستند و به عنوان قند نحوی برای کاربردهای مختلف حالت ارائه می شوند. شما باید کدی را انتخاب کنید که راحتترین کد را در فایلی که مینویسید تولید کند.
دستور by
delegate به واردات زیر نیاز دارد:
import androidx.compose.runtime.getValue
import androidx.compose.runtime.setValue
میتوانید از مقدار به خاطر سپردهشده بهعنوان پارامتری برای ترکیبپذیریهای دیگر یا حتی بهعنوان منطق در عبارات برای تغییر اینکه کدام ترکیبپذیر نمایش داده میشوند، استفاده کنید. به عنوان مثال، اگر نمی خواهید پیام تبریک را در صورت خالی بودن نام نمایش دهید، از حالت در عبارت if
استفاده کنید:
@Composable fun HelloContent() { Column(modifier = Modifier.padding(16.dp)) { var name by remember { mutableStateOf("") } if (name.isNotEmpty()) { Text( text = "Hello, $name!", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } ) } }
در حالی که remember
به شما کمک میکند حالت را در بین ترکیبهای مجدد حفظ کنید، وضعیت در تغییرات پیکربندی حفظ نمیشود. برای این کار باید از rememberSaveable
استفاده کنید. rememberSaveable
به طور خودکار هر مقداری را که می توان در یک Bundle
ذخیره کرد ذخیره می کند. برای مقادیر دیگر، میتوانید یک شی محافظ سفارشی ارسال کنید.
سایر انواع ایالت های پشتیبانی شده
نوشتن نیازی به استفاده از MutableState<T>
برای نگه داشتن حالت ندارد. از دیگر انواع قابل مشاهده پشتیبانی می کند. قبل از خواندن یک نوع قابل مشاهده دیگر در Compose، باید آن را به State<T>
تبدیل کنید تا وقتی که حالت تغییر می کند، composable ها بتوانند به طور خودکار دوباره ترکیب شوند.
کشتی هایی با توابع برای ایجاد State<T>
از انواع معمولی قابل مشاهده که در برنامه های Android استفاده می شود بنویسید. قبل از استفاده از این ادغام ها، مصنوع(های) مناسب را مطابق زیر اضافه کنید:
Flow
:collectAsStateWithLifecycle()
collectAsStateWithLifecycle()
مقادیر را از یکFlow
به روشی آگاه از چرخه زندگی جمعآوری میکند و به برنامه شما اجازه میدهد منابع برنامه را حفظ کند. این نشان دهنده آخرین مقدار منتشر شده ازState
Compose است. از این API به عنوان روش توصیه شده برای جمع آوری جریان ها در برنامه های Android استفاده کنید.وابستگی زیر در فایل
build.gradle
لازم است (باید 2.6.0-beta01 یا جدیدتر باشد):
کاتلین
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7")
}
شیار
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.7"
}
collectAsState
شبیهcollectAsStateWithLifecycle
است، زیرا مقادیر یکFlow
را نیز جمع آوری می کند و آن را بهState
Compose تبدیل می کند.به جای
collectAsStateWithLifecycle
، که فقط برای اندروید است، ازcollectAsState
برای کدهای پلتفرم آگنوستیک استفاده کنید.وابستگی های اضافی برای
collectAsState
لازم نیست، زیرا درcompose-runtime
در دسترس است.observeAsState()
شروع به مشاهده اینLiveData
می کند و مقادیر آن را از طریقState
نشان می دهد.وابستگی زیر در فایل
build.gradle
مورد نیاز است:
کاتلین
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.7.5")
}
شیار
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.7.5"
}
subscribeAsState()
توابع پسوندی هستند که جریانهای واکنشی RxJava2 (به عنوان مثالSingle
,Observable
,Completable
) را به ComposeState
تبدیل می کنند.وابستگی زیر در فایل
build.gradle
مورد نیاز است:
کاتلین
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.7.5")
}
شیار
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.7.5"
}
subscribeAsState()
توابع پسوندی هستند که جریانهای واکنشی RxJava3 (به عنوان مثالSingle
,Observable
,Completable
) را به ComposeState
تبدیل می کنند.وابستگی زیر در فایل
build.gradle
مورد نیاز است:
کاتلین
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.7.5")
}
شیار
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.7.5"
}
دولتی در مقابل بی تابعیتی
یک Composable که از remember
برای ذخیره یک شی استفاده میکند، حالت داخلی ایجاد میکند و حالت composable را حالت میکند. HelloContent
نمونه ای از یک حالت ترکیبی است زیرا وضعیت name
خود را در داخل نگه می دارد و تغییر می دهد. این می تواند در مواقعی مفید باشد که تماس گیرنده نیازی به کنترل حالت ندارد و می تواند بدون نیاز به مدیریت وضعیت خود از آن استفاده کند. با این حال، ترکیبات با حالت داخلی تمایل کمتری برای استفاده مجدد دارند و آزمایش آنها سخت تر است.
ترکیبپذیر بدون حالت ، ترکیبپذیری است که هیچ حالتی ندارد. یک راه آسان برای دستیابی به حالت بدون تابعیت استفاده از بالابرهای دولتی است.
همانطور که شما قابلیتهای قابل استفاده مجدد را توسعه میدهید، اغلب میخواهید هم یک نسخه حالت دار و هم یک نسخه بدون حالت از یک قابلیت ترکیبسازی را در معرض نمایش قرار دهید. نسخه Stateful برای تماسگیرندگانی که به وضعیت اهمیتی نمیدهند مناسب است و نسخه بدون حالت برای تماسگیرندگانی که نیاز به کنترل یا بالا بردن وضعیت دارند، ضروری است.
بالابر دولتی
بالا بردن حالت در Compose الگویی از حرکت حالت به فراخوان کننده یک Composable برای ایجاد حالت Composable بدون حالت است. الگوی کلی برای بالا بردن حالت در Jetpack Compose جایگزینی متغیر حالت با دو پارامتر است:
-
value: T
: مقدار فعلی برای نمایش -
onValueChange: (T) -> Unit
: رویدادی که درخواست تغییر مقدار میکند، جایی کهT
مقدار جدید پیشنهادی است.
با این حال، شما محدود به onValueChange
نیستید. اگر رویدادهای خاص تری برای ترکیب بندی مناسب هستند، باید آنها را با استفاده از لامبدا تعریف کنید.
حالتی که به این روش بالا می رود دارای چند ویژگی مهم است:
- منبع منفرد حقیقت: با جابجایی حالت به جای تکرار آن، اطمینان حاصل می کنیم که تنها یک منبع حقیقت وجود دارد. این به جلوگیری از اشکالات کمک می کند.
- کپسوله شده: فقط ترکیبپذیرهای حالت دار میتوانند حالت خود را تغییر دهند. کاملا داخلیه
- قابل اشتراکگذاری: حالت Hoisted را میتوان با چندین ترکیب قابل اشتراکگذاری به اشتراک گذاشت. اگر میخواهید
name
با یک ترکیب متفاوت بخوانید، بالا بردن این امکان را به شما میدهد. - قابل رهگیری: تماس گیرندگان با قابلیت های ترکیبی بدون حالت می توانند تصمیم بگیرند که رویدادها را قبل از تغییر حالت نادیده بگیرند یا تغییر دهند.
- Decoupled: حالت برای ترکیبات بدون حالت ممکن است در هر جایی ذخیره شود. برای مثال، اکنون امکان انتقال
name
بهViewModel
وجود دارد.
در مورد مثال، name
و onValueChange
از HelloContent
استخراج میکنید و آنها را به بالای درخت به یک HelloScreen
که HelloContent
را فراخوانی میکند منتقل میکنید.
@Composable fun HelloScreen() { var name by rememberSaveable { mutableStateOf("") } HelloContent(name = name, onNameChange = { name = it }) } @Composable fun HelloContent(name: String, onNameChange: (String) -> Unit) { Column(modifier = Modifier.padding(16.dp)) { Text( text = "Hello, $name", modifier = Modifier.padding(bottom = 8.dp), style = MaterialTheme.typography.bodyMedium ) OutlinedTextField(value = name, onValueChange = onNameChange, label = { Text("Name") }) } }
با بالا بردن حالت از HelloContent
، استدلال در مورد ترکیبپذیر، استفاده مجدد از آن در موقعیتهای مختلف و آزمایش آسانتر است. HelloContent
از نحوه ذخیره وضعیت آن جدا شده است. جداسازی به این معنی است که اگر HelloScreen
تغییر دهید یا جایگزین کنید، لازم نیست نحوه اجرای HelloContent
را تغییر دهید.
الگویی که در آن حالت پایین میآید و رویدادها بالا میروند ، جریان دادههای یک طرفه نامیده میشود. در این حالت، وضعیت از HelloScreen
به HelloContent
پایین میآید و رویدادها از HelloContent
به HelloScreen
بالا میروند. با دنبال کردن جریان دادههای یکطرفه، میتوانید ترکیبپذیرهایی را که حالت نمایش در رابط کاربری را نشان میدهند از بخشهایی از برنامه خود که ذخیره میکنند و حالت را تغییر میدهند جدا کنید.
برای کسب اطلاعات بیشتر به صفحه ایالت کجا باید بالا برید مراجعه کنید.
در حال بازیابی حالت در Compose
API rememberSaveable
برای remember
رفتار مشابهی دارد زیرا حالت را در بین ترکیببندیهای مجدد و همچنین در سراسر فعالیت یا بازآفرینی فرآیند با استفاده از مکانیسم حالت نمونه ذخیره شده حفظ میکند. به عنوان مثال، این اتفاق می افتد، زمانی که صفحه نمایش چرخانده می شود.
راه های ذخیره سازی حالت
تمام انواع دادههایی که به Bundle
اضافه میشوند بهطور خودکار ذخیره میشوند. اگر می خواهید چیزی را ذخیره کنید که نمی تواند به Bundle
اضافه شود، چندین گزینه وجود دارد.
بسته بندی کنید
ساده ترین راه حل اضافه کردن حاشیه نویسی @Parcelize
به شی است. شیء قابل بسته بندی می شود و می توان آن را باندل کرد. به عنوان مثال، این کد یک نوع داده City
parcelable می سازد و آن را در حالت ذخیره می کند.
@Parcelize data class City(val name: String, val country: String) : Parcelable @Composable fun CityScreen() { var selectedCity = rememberSaveable { mutableStateOf(City("Madrid", "Spain")) } }
MapSaver
اگر به دلایلی @Parcelize
مناسب نیست، می توانید از mapSaver
برای تعریف قانون خود برای تبدیل یک شی به مجموعه ای از مقادیر استفاده کنید که سیستم می تواند در Bundle
ذخیره کند.
data class City(val name: String, val country: String) val CitySaver = run { val nameKey = "Name" val countryKey = "Country" mapSaver( save = { mapOf(nameKey to it.name, countryKey to it.country) }, restore = { City(it[nameKey] as String, it[countryKey] as String) } ) } @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
ListSaver
برای جلوگیری از نیاز به تعریف کلیدها برای نقشه، می توانید listSaver
نیز استفاده کنید و از شاخص های آن به عنوان کلید استفاده کنید:
data class City(val name: String, val country: String) val CitySaver = listSaver<City, Any>( save = { listOf(it.name, it.country) }, restore = { City(it[0] as String, it[1] as String) } ) @Composable fun CityScreen() { var selectedCity = rememberSaveable(stateSaver = CitySaver) { mutableStateOf(City("Madrid", "Spain")) } }
دارندگان ایالت در Compose
بالا بردن حالت ساده را می توان در خود توابع ترکیبی مدیریت کرد. با این حال، اگر مقدار حالت برای پیگیری افزایش یابد، یا منطقی که باید در توابع ترکیبپذیر انجام شود، کار خوبی است که مسئولیتهای منطقی و حالت را به کلاسهای دیگر واگذار کنید: دارندگان حالت .
برای کسب اطلاعات بیشتر، بالا بردن حالت را در مستندات Compose یا به طور کلیتر، صفحه دارندگان State و UI State را در راهنمای معماری ببینید.
هنگامی که کلیدها تغییر می کنند، محاسبات را به خاطر بسپارید
remember
API اغلب همراه با MutableState
استفاده می شود:
var name by remember { mutableStateOf("") }
در اینجا، استفاده از تابع remember
باعث میشود که مقدار MutableState
در ترکیب مجدد باقی بماند.
به طور کلی، remember
که یک پارامتر لامبدا calculation
می کند. هنگامی که remember
برای اولین بار اجرا می شود، calculation
لامبدا را فراخوانی می کند و نتیجه آن را ذخیره می کند. در طول ترکیب مجدد، remember
که مقداری را که آخرین بار ذخیره شده است، برمی گرداند.
جدا از حالت کش، میتوانید remember
برای ذخیره هر شی یا نتیجه عملیاتی در Composition استفاده کنید که مقداردهی اولیه یا محاسبه آن گران است. ممکن است نخواهید این محاسبه را در هر ترکیب مجدد تکرار کنید. یک مثال ایجاد این شی ShaderBrush
است که یک عملیات گران قیمت است:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
remember
مقدار را تا زمانی که از Composition خارج شود ذخیره می کند. با این حال، راهی برای باطل کردن مقدار ذخیره شده وجود دارد. remember
API نیز یک پارامتر key
یا keys
را می گیرد. اگر هر یک از این کلیدها تغییر کرد، دفعه بعد که تابع دوباره ترکیب شد ، remember
که حافظه پنهان را باطل می کند و بلوک لامبدا محاسبه را دوباره اجرا می کند . این مکانیسم به شما امکان کنترل طول عمر یک شی در Composition را می دهد. محاسبه تا زمانی که ورودیها تغییر نکنند معتبر میماند، نه تا زمانی که مقدار به خاطر سپرده شده از ترکیب خارج شود.
مثال های زیر نحوه عملکرد این مکانیسم را نشان می دهد.
در این قطعه، ShaderBrush
ایجاد شده و به عنوان رنگ پسزمینه یک Box
composable استفاده میشود. remember
که نمونه ShaderBrush
را ذخیره می کند زیرا همانطور که قبلا توضیح داده شد، بازسازی آن گران است. remember
که avatarRes
به عنوان پارامتر key1
می گیرد که تصویر پس زمینه انتخاب شده است. اگر avatarRes
تغییر کند، قلم مو با تصویر جدید دوباره ترکیب میشود و دوباره روی Box
اعمال میشود. این می تواند زمانی رخ دهد که کاربر تصویر دیگری را به عنوان پس زمینه از یک انتخابگر انتخاب کند.
@Composable private fun BackgroundBanner( @DrawableRes avatarRes: Int, modifier: Modifier = Modifier, res: Resources = LocalContext.current.resources ) { val brush = remember(key1 = avatarRes) { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) } Box( modifier = modifier.background(brush) ) { /* ... */ } }
در قطعه بعدی، state به کلاس دارنده حالت ساده MyAppState
بالا می رود. این یک تابع rememberMyAppState
را برای مقداردهی اولیه یک نمونه از کلاس با استفاده از remember
نمایش می دهد. نمایش چنین توابعی برای ایجاد نمونه ای که از ترکیب مجدد جان سالم به در می برد، یک الگوی رایج در Compose است. تابع rememberMyAppState
windowSizeClass
دریافت می کند که به عنوان پارامتر key
برای remember
عمل می کند. اگر این پارامتر تغییر کند، برنامه باید کلاس دارنده حالت ساده را با آخرین مقدار دوباره ایجاد کند. برای مثال، اگر کاربر دستگاه را بچرخاند، ممکن است این اتفاق بیفتد.
@Composable private fun rememberMyAppState( windowSizeClass: WindowSizeClass ): MyAppState { return remember(windowSizeClass) { MyAppState(windowSizeClass) } } @Stable class MyAppState( private val windowSizeClass: WindowSizeClass ) { /* ... */ }
Compose از پیاده سازی برابرهای کلاس برای تصمیم گیری در مورد تغییر یک کلید و بی اعتبار کردن مقدار ذخیره شده استفاده می کند.
وضعیت ذخیره با کلیدهای فراتر از ترکیب مجدد
API rememberSaveable
remember
است که می تواند داده ها را در یک Bundle
ذخیره کند. این API به حالت اجازه می دهد تا نه تنها از ترکیب مجدد، بلکه از فعالیت های تفریحی و مرگ فرآیند آغاز شده توسط سیستم نیز زنده بماند. rememberSaveable
پارامترهای input
را برای همان هدفی که remember
keys
دریافت دریافت می کند، دریافت می کند. هنگامی که هر یک از ورودی ها تغییر می کند، حافظه پنهان باطل می شود . دفعه بعد که تابع دوباره ترکیب می شود، rememberSaveable
بلوک لامبدا محاسبه را دوباره اجرا می کند.
در مثال زیر، rememberSaveable
userTypedQuery
ذخیره می کند تا زمانی که typedQuery
تغییر کند:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
بیشتر بدانید
برای کسب اطلاعات بیشتر در مورد State و Jetpack Compose، به منابع اضافی زیر مراجعه کنید.
نمونه ها
Codelabs
ویدیوها
وبلاگ ها
{% کلمه به کلمه %}برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- معماری UI Compose شما
- وضعیت رابط کاربری را در Compose ذخیره کنید
- عوارض جانبی در Compose