State و Jetpack Compose

حالت در برنامه هر مقداری است که می تواند در طول زمان تغییر کند. این یک تعریف بسیار گسترده است و همه چیز را از یک پایگاه داده اتاق گرفته تا یک متغیر در یک کلاس را در بر می گیرد.

همه برنامه های اندروید وضعیت را به کاربر نمایش می دهند. چند نمونه از حالت در برنامه های اندروید:

  • یک نوار اسنک که نشان می دهد چه زمانی اتصال شبکه نمی تواند برقرار شود.
  • یک پست وبلاگ و نظرات مرتبط.
  • انیمیشن‌ها را روی دکمه‌هایی که وقتی کاربر روی آنها کلیک می‌کند پخش می‌شوند.
  • برچسب هایی که کاربر می تواند روی یک تصویر بکشد.

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"
}
  • Flow : collectAsState()

    collectAsState شبیه collectAsStateWithLifecycle است، زیرا مقادیر یک Flow را نیز جمع آوری می کند و آن را به State Compose تبدیل می کند.

    به جای collectAsStateWithLifecycle ، که فقط برای اندروید است، از collectAsState برای کدهای پلتفرم آگنوستیک استفاده کنید.

    وابستگی های اضافی برای collectAsState لازم نیست، زیرا در compose-runtime در دسترس است.

  • LiveData : observeAsState()

    observeAsState() شروع به مشاهده این LiveData می کند و مقادیر آن را از طریق State نشان می دهد.

    وابستگی زیر در فایل build.gradle مورد نیاز است:

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-livedata:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-livedata:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava2:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava2:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava3:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava3:1.8.1"
}

دولتی در مقابل بی تابعیتی

یک 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

ویدیوها

وبلاگ ها

{% کلمه به کلمه %} {% آخر کلمه %} {% کلمه به کلمه %} {% endverbatim %}،

حالت در برنامه هر مقداری است که می تواند در طول زمان تغییر کند. این یک تعریف بسیار گسترده است و همه چیز را از یک پایگاه داده اتاق گرفته تا یک متغیر در یک کلاس را در بر می گیرد.

همه برنامه های اندروید وضعیت را به کاربر نمایش می دهند. چند نمونه از حالت در برنامه های اندروید:

  • یک نوار اسنک که نشان می دهد چه زمانی اتصال شبکه نمی تواند برقرار شود.
  • یک پست وبلاگ و نظرات مرتبط.
  • انیمیشن‌ها را روی دکمه‌هایی که وقتی کاربر روی آنها کلیک می‌کند پخش می‌شوند.
  • برچسب هایی که کاربر می تواند روی یک تصویر بکشد.

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"
}
  • Flow : collectAsState()

    collectAsState شبیه collectAsStateWithLifecycle است، زیرا مقادیر یک Flow را نیز جمع آوری می کند و آن را به State Compose تبدیل می کند.

    به جای collectAsStateWithLifecycle ، که فقط برای اندروید است، از collectAsState برای کدهای پلتفرم آگنوستیک استفاده کنید.

    وابستگی های اضافی برای collectAsState لازم نیست، زیرا در compose-runtime در دسترس است.

  • LiveData : observeAsState()

    observeAsState() شروع به مشاهده این LiveData می کند و مقادیر آن را از طریق State نشان می دهد.

    وابستگی زیر در فایل build.gradle مورد نیاز است:

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-livedata:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-livedata:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava2:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava2:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava3:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava3:1.8.1"
}

دولتی در مقابل بی تابعیتی

یک 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 که قابل استفاده مجدد بلوک لامبدا محاسبه می شود.

در مثال زیر ، فروشگاه های قابل حمل userTypedQuery rememberSaveable تا زمانی که typedQuery تغییر کند:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

بیشتر بدانید

برای کسب اطلاعات بیشتر در مورد Constate State و JetPack ، با منابع اضافی زیر مشورت کنید.

نمونه ها

Codelabs

ویدیوها

وبلاگ ها

{٪ کلمه ٪} {٪ EndverBatim ٪} {٪ کلمه ٪} {٪ EndverBatim ٪} ،

حالت در یک برنامه هر ارزشی است که می تواند با گذشت زمان تغییر کند. این یک تعریف بسیار گسترده است و همه چیز را از یک پایگاه داده اتاق گرفته تا یک متغیر در یک کلاس در بر می گیرد.

همه برنامه های Android وضعیت را به کاربر نشان می دهند. چند نمونه از حالت در برنامه های Android:

  • اسنکبر که نشان می دهد چه زمانی نمی توان اتصال شبکه برقرار کرد.
  • یک پست وبلاگ و نظرات مرتبط.
  • انیمیشن های ریپل روی دکمه هایی که وقتی کاربر روی آنها کلیک می کند بازی می کنند.
  • برچسب هایی که کاربر می تواند در بالای یک تصویر ترسیم کند.

JetPack Compose به شما کمک می کند تا در مورد مکان و نحوه ذخیره و استفاده از حالت در یک برنامه Android صریح باشید. این راهنما به ارتباط بین حالت و ترکیبات و API هایی که Jetpack Compose ارائه می دهد برای کار راحت تر با حالت ارائه می دهد.

حالت و ترکیب

آهنگسازی اعلامی است و به همین ترتیب تنها راه به روزرسانی آن با فراخوانی همان ترکیب با استدلال های جدید است. این استدلال ها بازنمایی از دولت UI است. هر زمان که یک حالت به روز شود ، بازپرداخت صورت می گیرد. در نتیجه ، مواردی مانند TextField به طور خودکار مانند آنچه در نماهای مبتنی بر XML انجام می شود به روز نمی شوند. یک ترکیب باید صریحاً به حالت جدید گفته شود تا بتواند بر این اساس به روز شود.

@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 آن تغییر می کند ، به روز می شود. این به دلیل نحوه کار ترکیب و بازپرداخت در آهنگسازی است.

برای کسب اطلاعات بیشتر در مورد ترکیب اولیه و تجدید نظر ، به تفکر در آهنگسازی مراجعه کنید.

حالت در ترکیبات

توابع کامپوزیت می توانند از API remember تا یک شی را در حافظه ذخیره کنید. مقداری محاسبه شده توسط remember در طول ترکیب اولیه در ترکیب ذخیره می شود و مقدار ذخیره شده در طول بازپرداخت بازگردانده می شود. remember می توانید برای ذخیره هر دو اشیاء قابل تغییر و تغییر ناپذیر استفاده کنید.

mutableStateOf یک MutableState<T> ایجاد می کند ، که یک نوع قابل مشاهده است که با زمان اجرای آهنگسازی یکپارچه شده است.

interface MutableState<T> : State<T> {
    override var value: T
}

هرگونه تغییر در برنامه های value ، بازآفرینی از هر کارکرد ترکیب را که value می خوانند ، می کند.

سه روش برای اعلام یک شیء MutableState در یک ترکیب وجود دارد:

  • val mutableState = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (value, setValue) = remember { mutableStateOf(default) }

این اعلامیه ها معادل هستند و به عنوان قند نحوی برای استفاده های مختلف دولت ارائه می شوند. شما باید یکی از مواردی را انتخاب کنید که ساده ترین کد خواندن را در آهنگسازی که می نویسید تولید می کند.

نحو by نماینده به واردات زیر نیاز دارد:

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> برای نگه داشتن حالت ندارد. از انواع دیگر قابل مشاهده پشتیبانی می کند. قبل از خواندن نوع قابل مشاهده دیگری در آهنگسازی ، باید آن را به State<T> تبدیل کنید تا در صورت تغییر حالت ، آهنگسازان بتوانند به طور خودکار دوباره درخواست کنند.

کشتی ها را با توابع برای ایجاد State<T> از انواع قابل مشاهده متداول که در برنامه های اندرویدی استفاده می شود ، تهیه کنید. قبل از استفاده از این ادغام ها ، مصنوعات (های) مناسب را مطابق شکل زیر اضافه کنید:

  • Flow : collectAsStateWithLifecycle()

    collectAsStateWithLifecycle() مقادیر را از یک Flow به صورت آگاه از چرخه عمر جمع می کند و به برنامه شما اجازه می دهد تا منابع برنامه را حفظ کند. این نشان دهنده آخرین مقدار ساطع شده از State آهنگسازی است. از این 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"
}
  • Flow : collectAsState()

    collectAsState شبیه به collectAsStateWithLifecycle است ، زیرا مقادیر را از یک Flow نیز جمع می کند و آن را به State آهنگسازی تبدیل می کند.

    به جای collectAsStateWithLifecycle ، که فقط اندرویدی است ، از collectAsState برای کد پلتفرم-آگنوستیک استفاده کنید.

    وابستگی های اضافی برای collectAsState لازم نیست ، زیرا در compose-runtime در دسترس است.

  • LiveData : observeAsState()

    observeAsState() مشاهده این LiveData شروع می کند و ارزش های آن را از طریق State نشان می دهد.

    وابستگی زیر در پرونده build.gradle مورد نیاز است:

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-livedata:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-livedata:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava2:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava2:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava3:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava3:1.8.1"
}

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

یک ترکیب که از remember برای ذخیره یک شی استفاده می کند ، وضعیت داخلی ایجاد می کند و باعث می شود که این ترکیب مطبوع باشد . HelloContent نمونه ای از یک ترکیب مطبوع است زیرا وضعیت name خود را در داخل کشور نگه می دارد و اصلاح می کند. این می تواند در شرایطی مفید باشد که یک تماس گیرنده نیازی به کنترل دولت ندارد و می تواند بدون نیاز به مدیریت خود دولت از آن استفاده کند. با این حال ، ترکیبات با وضعیت داخلی تمایل به استفاده مجدد و آزمایش سخت تر دارند.

یک ترکیب بدون تابعیت یک ترکیب است که هیچ وضعیتی را در خود جای نمی دهد. یک راه آسان برای دستیابی به بی تابعیت استفاده از بالا بردن دولت است.

از آنجا که ساختارهای قابل استفاده مجدد را توسعه می دهید ، اغلب می خواهید یک نسخه دولتی و بدون تابعیت از همان آهنگسازی را در معرض دید قرار دهید. نسخه مطبوع برای تماس گیرنده هایی که به دولت اهمیتی نمی دهند مناسب است و نسخه بدون تابعیت برای تماس گیرنده هایی که نیاز به کنترل یا بلند کردن دولت دارند ضروری است.

دولت بالایی

بلند کردن دولت در آهنگسازی الگویی برای انتقال حالت به تماس گیرنده یک آهنگساز برای ایجاد یک کمبود تابعیت است. الگوی کلی برای بلند کردن حالت در آهنگسازی JetPack ، جایگزینی متغیر حالت با دو پارامتر است:

  • value: T : مقدار فعلی برای نمایش
  • onValueChange: (T) -> Unit : رویدادی که ارزش تغییر را دارد ، جایی که T مقدار جدید پیشنهادی است

با این حال ، شما محدود به onValueChange نیستید. اگر رویدادهای خاص تر برای ترکیب مناسب هستند ، باید آنها را با استفاده از لامبدا تعریف کنید.

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

  • منبع واحد حقیقت: با حرکت به جای کپی کردن آن ، ما اطمینان حاصل می کنیم که تنها یک منبع حقیقت وجود دارد. این به جلوگیری از اشکالات کمک می کند.
  • محاصره شده: فقط آهنگسازان مطرح می توانند وضعیت خود را اصلاح کنند. کاملاً داخلی است.
  • اشتراکی: حالت بلند شده را می توان با چندین ترکیب مشترک به اشتراک گذاشت. اگر می خواستید name در یک ترکیب متفاوت بخوانید ، بالا بردن به شما امکان می دهد این کار را انجام دهید.
  • رهگیری: تماس گیرندگان به ترکیبات بدون تابعیت می توانند تصمیم بگیرند که قبل از تغییر حالت ، وقایع را نادیده بگیرند یا اصلاح کنند.
  • جداشده: وضعیت ترکیبات بدون تابعیت ممکن است در هر جایی ذخیره شود. به عنوان مثال ، اکنون می توان 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 می روند. با پیروی از جریان داده های یک طرفه ، می توانید آهنگسازهایی را که در UI از قسمت های برنامه خود استفاده می کنند ، جدا کنید.

برای کسب اطلاعات بیشتر به صفحه حالت بالابر مراجعه کنید.

بازگرداندن حالت در آهنگسازی

API rememberSaveable که به طور مشابه به remember ، زیرا این حالت را در بین بازپرداخت ها حفظ می کند ، و همچنین در طول فعالیت یا تفریح ​​فرآیند با استفاده از مکانیسم حالت ذخیره شده ذخیره شده. به عنوان مثال ، این اتفاق می افتد ، هنگامی که صفحه چرخانده می شود.

راه های ذخیره ایالت

تمام انواع داده هایی که به Bundle اضافه می شوند به طور خودکار ذخیره می شوند. اگر می خواهید چیزی را ذخیره کنید که نمی تواند به Bundle اضافه شود ، گزینه های مختلفی وجود دارد.

بسته بندی کردن

ساده ترین راه حل اضافه کردن حاشیه نویسی @Parcelize به شی است. شیء قابل بسته شدن است و می تواند همراه باشد. به عنوان مثال ، این کد نوع داده های قابل حمل City را ایجاد می کند و آن را در ایالت ذخیره می کند.

@Parcelize
data class City(val name: String, val country: String) : Parcelable

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

ماشین ساز

اگر به دلایلی @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 نیز استفاده کرده و از شاخص های آن به عنوان کلیدها استفاده کنید:

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"))
    }
}

دارندگان ایالتی در آهنگسازی

بالابر حالت ساده را می توان در خود توابع ساختگی مدیریت کرد. با این حال ، اگر میزان وضعیت برای پیگیری افزایش ، یا منطق انجام در کارکردهای سازنده بوجود می آید ، این یک عمل خوب است که منطق و مسئولیت های دولت را به سایر کلاس ها واگذار کنید: دارندگان دولت .

برای کسب اطلاعات بیشتر ، به بالا بردن دولت در مستندات آهنگسازی یا به طور کلی ، دارندگان ایالتی و صفحه ایالتی UI در راهنمای معماری مراجعه کنید.

محاسبات هنگام تغییر کلیدها محاسبات را به خاطر بسپارید

API remember که اغلب همراه با MutableState استفاده می شود:

var name by remember { mutableStateOf("") }

در اینجا ، با استفاده از عملکرد remember ، مقدار MutableState باعث زنده ماندن مجدد می شود.

به طور کلی ، remember یک پارامتر لامبدا calculation می کند. هنگامی که remember اولین بار اجرا می شود ، calculation لامبدا را فراخوانی می کند و نتیجه خود را ذخیره می کند. در حین بازپرداخت ، remember مقداری را که آخرین بار در آن ذخیره شده بود باز می گرداند.

به غیر از حالت ذخیره ، می توانید remember برای ذخیره هر شی یا نتیجه عملیاتی در ترکیب استفاده کنید که برای اولیه سازی یا محاسبه گران است. ممکن است شما بخواهید این محاسبه را در هر بازپرداخت تکرار کنید. به عنوان مثال ، ایجاد این شیء ShaderBrush است که یک عملیات گران قیمت است:

val brush = remember {
    ShaderBrush(
        BitmapShader(
            ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(),
            Shader.TileMode.REPEAT,
            Shader.TileMode.REPEAT
        )
    )
}

remember مقدار را تا زمانی که ترکیب را ترک کند ، ذخیره می کند. با این حال ، راهی برای باطل کردن مقدار ذخیره شده وجود دارد. API remember همچنین یک پارامتر key یا keys را می گیرد. اگر هرکدام از این کلیدها تغییر می کنند ، دفعه بعد که عملکرد دوباره انجام می شود ، remember که حافظه نهان را باطل می کند و بلوک لامبدا محاسبه را دوباره اجرا می کند . این مکانیسم به شما امکان کنترل عمر یک شی را در ترکیب می دهد. محاسبه تا زمانی که ورودی ها تغییر کنند ، معتبر است ، به جای اینکه مقدار به یاد داشته باشید که ترکیب را ترک می کند.

مثالهای زیر نشان می دهد که چگونه این مکانیسم کار می کند.

در این قطعه ، یک ShaderBrush ایجاد شده و به عنوان رنگ پس زمینه یک Box قابل استفاده استفاده می شود. همانطور که در ابتدا توضیح داده شد ، 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)
    ) {
        /* ... */
    }
}

در قطعه بعدی ، ایالت به یک کلاس دارنده ایالتی ساده MyAppState بلند می شود. این یک تابع rememberMyAppState برای شروع نمونه ای از کلاس با استفاده از remember در معرض دید قرار می دهد. قرار گرفتن در معرض چنین توابع برای ایجاد نمونه ای که زنده مانده است ، یک الگوی مشترک در آهنگسازی است. تابع rememberMyAppState windowSizeClass دریافت می کند ، که به عنوان پارامتر key برای remember عمل می کند. اگر این پارامتر تغییر کند ، برنامه باید کلاس نگهدارنده حالت ساده را با جدیدترین مقدار بازآفرینی کند. این ممکن است رخ دهد اگر مثلاً کاربر دستگاه را بچرخاند.

@Composable
private fun rememberMyAppState(
    windowSizeClass: WindowSizeClass
): MyAppState {
    return remember(windowSizeClass) {
        MyAppState(windowSizeClass)
    }
}

@Stable
class MyAppState(
    private val windowSizeClass: WindowSizeClass
) { /* ... */ }

آهنگسازی از اجرای Equals Equals استفاده می کند تا تصمیم بگیرد که آیا یک کلید تغییر کرده و مقدار ذخیره شده را باطل می کند.

حالت را با کلیدهای فراتر از بازپرداخت ذخیره کنید

API rememberSaveable remember یک بسته بندی در اطراف است که می تواند داده ها را در یک Bundle ذخیره کند. این API به دولت اجازه می دهد تا نه تنها از بازپرداخت ، بلکه فعالیت های تفریحی فعالیت و مرگ فرآیند سیستم را نیز زنده بماند. rememberSaveable پارامترهای input را برای همان هدفی که remember keys دریافت می کند ، دریافت می کند. هنگامی که هر یک از ورودی ها تغییر می کنند ، حافظه پنهان باطل می شود . دفعه بعد که عملکرد دوباره انجام می شود ، rememberSaveable که قابل استفاده مجدد بلوک لامبدا محاسبه می شود.

در مثال زیر ، فروشگاه های قابل حمل userTypedQuery rememberSaveable تا زمانی که typedQuery تغییر کند:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

بیشتر بدانید

برای کسب اطلاعات بیشتر در مورد Constate State و JetPack ، با منابع اضافی زیر مشورت کنید.

نمونه ها

Codelabs

ویدیوها

وبلاگ ها

{٪ کلمه ٪} {٪ EndverBatim ٪} {٪ کلمه ٪} {٪ EndverBatim ٪} ،

حالت در یک برنامه هر ارزشی است که می تواند با گذشت زمان تغییر کند. این یک تعریف بسیار گسترده است و همه چیز را از یک پایگاه داده اتاق گرفته تا یک متغیر در یک کلاس در بر می گیرد.

همه برنامه های Android وضعیت را به کاربر نشان می دهند. چند نمونه از حالت در برنامه های Android:

  • اسنکبر که نشان می دهد چه زمانی نمی توان اتصال شبکه برقرار کرد.
  • یک پست وبلاگ و نظرات مرتبط.
  • انیمیشن های ریپل روی دکمه هایی که وقتی کاربر روی آنها کلیک می کند بازی می کنند.
  • برچسب هایی که کاربر می تواند در بالای یک تصویر ترسیم کند.

JetPack Compose به شما کمک می کند تا در مورد مکان و نحوه ذخیره و استفاده از حالت در یک برنامه Android صریح باشید. این راهنما به ارتباط بین حالت و ترکیبات و API هایی که Jetpack Compose ارائه می دهد برای کار راحت تر با حالت ارائه می دهد.

حالت و ترکیب

آهنگسازی اعلامی است و به همین ترتیب تنها راه به روزرسانی آن با فراخوانی همان ترکیب با استدلال های جدید است. این استدلال ها بازنمایی از دولت UI است. هر زمان که یک حالت به روز شود ، بازپرداخت صورت می گیرد. در نتیجه ، مواردی مانند TextField به طور خودکار مانند آنچه در نماهای مبتنی بر XML انجام می شود به روز نمی شوند. یک ترکیب باید صریحاً به حالت جدید گفته شود تا بتواند بر این اساس به روز شود.

@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 آن تغییر می کند ، به روز می شود. این به دلیل نحوه کار ترکیب و بازپرداخت در آهنگسازی است.

برای کسب اطلاعات بیشتر در مورد ترکیب اولیه و تجدید نظر ، به تفکر در آهنگسازی مراجعه کنید.

حالت در ترکیبات

توابع کامپوزیت می توانند از API remember تا یک شی را در حافظه ذخیره کنید. مقداری محاسبه شده توسط remember در طول ترکیب اولیه در ترکیب ذخیره می شود و مقدار ذخیره شده در طول بازپرداخت بازگردانده می شود. remember می توانید برای ذخیره هر دو اشیاء قابل تغییر و تغییر ناپذیر استفاده کنید.

mutableStateOf یک MutableState<T> ایجاد می کند ، که یک نوع قابل مشاهده است که با زمان اجرای آهنگسازی یکپارچه شده است.

interface MutableState<T> : State<T> {
    override var value: T
}

هرگونه تغییر در برنامه های value ، بازآفرینی از هر کارکرد ترکیب را که value می خوانند ، می کند.

سه روش برای اعلام یک شیء MutableState در یک ترکیب وجود دارد:

  • val mutableState = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (value, setValue) = remember { mutableStateOf(default) }

این اعلامیه ها معادل هستند و به عنوان قند نحوی برای استفاده های مختلف دولت ارائه می شوند. شما باید یکی از مواردی را انتخاب کنید که ساده ترین کد خواندن را در آهنگسازی که می نویسید تولید می کند.

نحو by نماینده به واردات زیر نیاز دارد:

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> برای نگه داشتن حالت ندارد. از انواع دیگر قابل مشاهده پشتیبانی می کند. قبل از خواندن نوع قابل مشاهده دیگری در آهنگسازی ، باید آن را به State<T> تبدیل کنید تا در صورت تغییر حالت ، آهنگسازان بتوانند به طور خودکار دوباره درخواست کنند.

کشتی ها را با توابع برای ایجاد State<T> از انواع قابل مشاهده متداول که در برنامه های اندرویدی استفاده می شود ، تهیه کنید. قبل از استفاده از این ادغام ها ، مصنوعات (های) مناسب را مطابق شکل زیر اضافه کنید:

  • Flow : collectAsStateWithLifecycle()

    collectAsStateWithLifecycle() مقادیر را از یک Flow به صورت آگاه از چرخه عمر جمع می کند و به برنامه شما اجازه می دهد تا منابع برنامه را حفظ کند. این نشان دهنده آخرین مقدار ساطع شده از State آهنگسازی است. از این 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"
}
  • Flow : collectAsState()

    collectAsState شبیه به collectAsStateWithLifecycle است ، زیرا مقادیر را از یک Flow نیز جمع می کند و آن را به State آهنگسازی تبدیل می کند.

    به جای collectAsStateWithLifecycle ، که فقط اندرویدی است ، از collectAsState برای کد پلتفرم-آگنوستیک استفاده کنید.

    وابستگی های اضافی برای collectAsState لازم نیست ، زیرا در compose-runtime در دسترس است.

  • LiveData : observeAsState()

    observeAsState() مشاهده این LiveData شروع می کند و ارزش های آن را از طریق State نشان می دهد.

    وابستگی زیر در پرونده build.gradle مورد نیاز است:

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-livedata:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-livedata:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava2:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava2:1.8.1"
}

کاتلین

dependencies {
      ...
      implementation("androidx.compose.runtime:runtime-rxjava3:1.8.1")
}

شیار

dependencies {
      ...
      implementation "androidx.compose.runtime:runtime-rxjava3:1.8.1"
}

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

یک ترکیب که از remember برای ذخیره یک شی استفاده می کند ، وضعیت داخلی ایجاد می کند و باعث می شود که این ترکیب مطبوع باشد . HelloContent نمونه ای از یک ترکیب مطبوع است زیرا وضعیت name خود را در داخل کشور نگه می دارد و اصلاح می کند. این می تواند در شرایطی مفید باشد که یک تماس گیرنده نیازی به کنترل دولت ندارد و می تواند بدون نیاز به مدیریت خود دولت از آن استفاده کند. با این حال ، ترکیبات با وضعیت داخلی تمایل به استفاده مجدد و آزمایش سخت تر دارند.

یک ترکیب بدون تابعیت یک ترکیب است که هیچ وضعیتی را در خود جای نمی دهد. یک راه آسان برای دستیابی به بی تابعیت استفاده از بالا بردن دولت است.

از آنجا که ساختارهای قابل استفاده مجدد را توسعه می دهید ، اغلب می خواهید یک نسخه دولتی و بدون تابعیت از همان آهنگسازی را در معرض دید قرار دهید. نسخه مطبوع برای تماس گیرنده هایی که به دولت اهمیتی نمی دهند مناسب است و نسخه بدون تابعیت برای تماس گیرنده هایی که نیاز به کنترل یا بلند کردن دولت دارند ضروری است.

دولت بالایی

بلند کردن دولت در آهنگسازی الگویی برای انتقال حالت به تماس گیرنده یک آهنگساز برای ایجاد یک کمبود تابعیت است. الگوی کلی برای بلند کردن حالت در آهنگسازی JetPack ، جایگزینی متغیر حالت با دو پارامتر است:

  • value: T : مقدار فعلی برای نمایش
  • onValueChange: (T) -> Unit : رویدادی که ارزش تغییر را دارد ، جایی که T مقدار جدید پیشنهادی است

با این حال ، شما محدود به onValueChange نیستید. اگر رویدادهای خاص تر برای ترکیب مناسب هستند ، باید آنها را با استفاده از لامبدا تعریف کنید.

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

  • منبع واحد حقیقت: با حرکت به جای کپی کردن آن ، ما اطمینان حاصل می کنیم که تنها یک منبع حقیقت وجود دارد. این به جلوگیری از اشکالات کمک می کند.
  • محاصره شده: فقط آهنگسازان مطرح می توانند وضعیت خود را اصلاح کنند. کاملاً داخلی است.
  • اشتراکی: حالت بلند شده را می توان با چندین ترکیب مشترک به اشتراک گذاشت. اگر می خواستید name در یک ترکیب متفاوت بخوانید ، بالا بردن به شما امکان می دهد این کار را انجام دهید.
  • رهگیری: تماس گیرندگان به ترکیبات بدون تابعیت می توانند تصمیم بگیرند که قبل از تغییر حالت ، وقایع را نادیده بگیرند یا اصلاح کنند.
  • جداشده: وضعیت ترکیبات بدون تابعیت ممکن است در هر جایی ذخیره شود. به عنوان مثال ، اکنون می توان 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 می روند. با پیروی از جریان داده های یک طرفه ، می توانید آهنگسازهایی را که در UI از قسمت های برنامه خود استفاده می کنند ، جدا کنید.

برای کسب اطلاعات بیشتر به صفحه حالت بالابر مراجعه کنید.

بازگرداندن حالت در آهنگسازی

API rememberSaveable که به طور مشابه به remember ، زیرا این حالت را در بین بازپرداخت ها حفظ می کند ، و همچنین در طول فعالیت یا تفریح ​​فرآیند با استفاده از مکانیسم حالت ذخیره شده ذخیره شده. به عنوان مثال ، این اتفاق می افتد ، هنگامی که صفحه چرخانده می شود.

راه های ذخیره ایالت

تمام انواع داده هایی که به Bundle اضافه می شوند به طور خودکار ذخیره می شوند. اگر می خواهید چیزی را ذخیره کنید که نمی تواند به Bundle اضافه شود ، گزینه های مختلفی وجود دارد.

بسته بندی کردن

ساده ترین راه حل اضافه کردن حاشیه نویسی @Parcelize به شی است. شیء قابل بسته شدن است و می تواند همراه باشد. به عنوان مثال ، این کد نوع داده های قابل حمل City را ایجاد می کند و آن را در ایالت ذخیره می کند.

@Parcelize
data class City(val name: String, val country: String) : Parcelable

@Composable
fun CityScreen() {
    var selectedCity = rememberSaveable {
        mutableStateOf(City("Madrid", "Spain"))
    }
}

ماشین ساز

اگر به دلایلی @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 نیز استفاده کرده و از شاخص های آن به عنوان کلیدها استفاده کنید:

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"))
    }
}

دارندگان ایالتی در آهنگسازی

بالابر حالت ساده را می توان در خود توابع ساختگی مدیریت کرد. با این حال ، اگر میزان وضعیت برای پیگیری افزایش ، یا منطق انجام در کارکردهای سازنده بوجود می آید ، این یک عمل خوب است که منطق و مسئولیت های دولت را به سایر کلاس ها واگذار کنید: دارندگان دولت .

برای کسب اطلاعات بیشتر ، به بالا بردن دولت در مستندات آهنگسازی یا به طور کلی ، دارندگان ایالتی و صفحه ایالتی UI در راهنمای معماری مراجعه کنید.

محاسبات هنگام تغییر کلیدها محاسبات را به خاطر بسپارید

API remember که اغلب همراه با MutableState استفاده می شود:

var name by remember { mutableStateOf("") }

در اینجا ، با استفاده از عملکرد remember ، مقدار MutableState باعث زنده ماندن مجدد می شود.

به طور کلی ، remember یک پارامتر لامبدا calculation می کند. هنگامی که remember اولین بار اجرا می شود ، calculation لامبدا را فراخوانی می کند و نتیجه خود را ذخیره می کند. در حین بازپرداخت ، remember مقداری را که آخرین بار در آن ذخیره شده بود باز می گرداند.

به غیر از حالت ذخیره ، می توانید remember برای ذخیره هر شی یا نتیجه عملیاتی در ترکیب استفاده کنید که برای اولیه سازی یا محاسبه گران است. ممکن است شما بخواهید این محاسبه را در هر بازپرداخت تکرار کنید. به عنوان مثال ، ایجاد این شیء ShaderBrush است که یک عملیات گران قیمت است:

val brush = remember {
    ShaderBrush(
        BitmapShader(
            ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(),
            Shader.TileMode.REPEAT,
            Shader.TileMode.REPEAT
        )
    )
}

remember مقدار را تا زمانی که ترکیب را ترک کند ، ذخیره می کند. با این حال ، راهی برای باطل کردن مقدار ذخیره شده وجود دارد. API remember همچنین یک پارامتر key یا keys را می گیرد. اگر هرکدام از این کلیدها تغییر می کنند ، دفعه بعد که عملکرد دوباره انجام می شود ، remember که حافظه نهان را باطل می کند و بلوک لامبدا محاسبه را دوباره اجرا می کند . این مکانیسم به شما امکان کنترل عمر یک شی را در ترکیب می دهد. محاسبه تا زمانی که ورودی ها تغییر کنند ، معتبر است ، به جای اینکه مقدار به یاد داشته باشید که ترکیب را ترک می کند.

مثالهای زیر نشان می دهد که چگونه این مکانیسم کار می کند.

در این قطعه ، یک ShaderBrush ایجاد شده و به عنوان رنگ پس زمینه یک Box قابل استفاده استفاده می شود. همانطور که در ابتدا توضیح داده شد ، 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)
    ) {
        /* ... */
    }
}

در قطعه بعدی ، ایالت به یک کلاس دارنده ایالتی ساده MyAppState بلند می شود. این یک تابع rememberMyAppState برای شروع نمونه ای از کلاس با استفاده از remember در معرض دید قرار می دهد. قرار گرفتن در معرض چنین توابع برای ایجاد نمونه ای که زنده مانده است ، یک الگوی مشترک در آهنگسازی است. تابع rememberMyAppState windowSizeClass دریافت می کند ، که به عنوان پارامتر key برای remember عمل می کند. If this parameter changes, the app needs to recreate the plain state holder class with the latest value. This may occur if, for example, the user rotates the device.

@Composable
private fun rememberMyAppState(
    windowSizeClass: WindowSizeClass
): MyAppState {
    return remember(windowSizeClass) {
        MyAppState(windowSizeClass)
    }
}

@Stable
class MyAppState(
    private val windowSizeClass: WindowSizeClass
) { /* ... */ }

Compose uses the class's equals implementation to decide if a key has changed and invalidate the stored value.

Store state with keys beyond recomposition

The rememberSaveable API is a wrapper around remember that can store data in a Bundle . This API allows state to survive not only recomposition, but also activity recreation and system-initiated process death. rememberSaveable receives input parameters for the same purpose that remember receives keys . The cache is invalidated when any of the inputs change . The next time the function recomposes, rememberSaveable re-executes the calculation lambda block.

In the following example, rememberSaveable stores userTypedQuery until typedQuery changes:

var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) {
    mutableStateOf(
        TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length))
    )
}

بیشتر بدانید

To learn more about state and Jetpack Compose, consult the following additional resources.

نمونه ها

Codelabs

ویدیوها

وبلاگ ها

{% verbatim %} {% endverbatim %} {% verbatim %} {% endverbatim %}