الحالة في التطبيق هي أي قيمة يمكن أن تتغيّر بمرور الوقت. هذا تعريف واسع جدًا ويشمل كل شيء من قاعدة بيانات الغرفة إلى متغير في فئة ما.
تعرض جميع تطبيقات Android للمستخدم الحالة. إليك بعض الأمثلة على الحالة في تطبيقات Android:
- شريط Snackbar يعرض عندما يتعذّر إنشاء اتصال بالشبكة
- مشاركة مدونة والتعليقات المرتبطة بها
- حركات مموجة على الأزرار التي يتم تشغيلها عندما ينقر المستخدم عليها.
- الملصقات التي يمكن للمستخدم رسمها فوق صورة.
يساعدك Jetpack Compose في توضيح مكان وطريقة التخزين والاستخدام في تطبيق Android. يركّز هذا الدليل على الربط بين حالة العناصر والعناصر القابلة للإنشاء، وعلى واجهات برمجة التطبيقات التي تتيح Jetpack Compose العمل بها بسهولة أكبر.
الحالة والتكوين
طريقة Compose وصفية، ومن ثمَّ تكون الطريقة الوحيدة لتعديلها هي استدعاء الدالة نفسها القابلة للإنشاء باستخدام وسيطات جديدة. هذه الوسيطات هي تمثيلات
لحالة واجهة المستخدم. كلما تم تعديل حالة، يتم إعادة التركيب. نتيجةً لذلك، لا يتم تعديل عناصر مثل 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
. ويرجع ذلك إلى كيفية عمل الإنشاء وإعادة الإنشاء في Compose.
لمعرفة المزيد من المعلومات حول التأليف الأولي وإعادة التركيب، يرجى الاطّلاع على التفكير في Compose.
الحالة في عنصر قابل للإنشاء
يمكن للدوال القابلة للإنشاء استخدام
واجهة برمجة تطبيقات 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>
لتحديد الحالة، بل يتيح استخدام أنواع أخرى قابلة للملاحظة. قبل قراءة نوع آخر قابل للملاحظة في Compose، يجب تحويله إلى State<T>
حتى يمكن إعادة إنشاء العناصر القابلة للإنشاء تلقائيًا عند تغيير الحالة.
أنشئ سفنًا مع دوال لإنشاء State<T>
من الأنواع الشائعة
القابلة للملاحظة المستخدمة في تطبيقات Android. قبل استخدام عمليات الدمج هذه، أضِف الأدوات المناسبة كما هو موضّح أدناه:
Flow
:collectAsStateWithLifecycle()
يجمع
collectAsStateWithLifecycle()
القيم منFlow
بطريقة الوعي بمراحل النشاط، ما يسمح لتطبيقك بالحفاظ على موارد التطبيق. وتمثل أحدث قيمة منبعثة من رمز الإنشاءState
. يمكنك استخدام واجهة برمجة التطبيقات هذه باعتبارها الطريقة الموصى بها لجمع بيانات التدفق على تطبيقات Android.تكون التبعية التالية مطلوبة في ملف
build.gradle
(يجب أن تكون بالإصدار 2.6.0-beta01 أو إصدار أحدث):
Kotlin
dependencies {
...
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
}
رائع
dependencies {
...
implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.2"
}
-
تكون
collectAsState
معادِلةcollectAsStateWithLifecycle
، لأنها تجمع أيضًا القيم منFlow
وتحولها إلى إنشاءState
.استخدِم
collectAsState
للرمز البرمجي غير المتوافق مع النظام الأساسي بدلاً منcollectAsStateWithLifecycle
الذي يعتمد على Android فقط.لا يلزم توفير اعتماديات إضافية لـ
collectAsState
، لأنها متوفرة فيcompose-runtime
. -
يبدأ
observeAsState()
في ملاحظةLiveData
هذا ويمثل قيمه من خلالState
.الاعتمادية التالية مطلوبة في ملف
build.gradle
:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-livedata:1.6.8")
}
رائع
dependencies {
...
implementation "androidx.compose.runtime:runtime-livedata:1.6.8"
}
-
subscribeAsState()
هي دوال إضافة تحوِّل ساحة المشاركات التفاعلية RxJava2 (مثلSingle
وObservable
وCompletable
) إلى "إنشاء"State
.الاعتمادية التالية مطلوبة في ملف
build.gradle
:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava2:1.6.8")
}
رائع
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava2:1.6.8"
}
-
subscribeAsState()
هي دوال إضافة تحوِّل أحداث البث التفاعلية في RxJava3 (مثلSingle
وObservable
وCompletable
) إلى "إنشاء"State
.الاعتمادية التالية مطلوبة في ملف
build.gradle
:
Kotlin
dependencies {
...
implementation("androidx.compose.runtime:runtime-rxjava3:1.6.8")
}
رائع
dependencies {
...
implementation "androidx.compose.runtime:runtime-rxjava3:1.6.8"
}
حالة تسجيل الدخول مقابل الحالة غير المُسجّلة
يؤدي إنشاء عنصر قابل للإنشاء يستخدم remember
لتخزين عنصر إلى إنشاء حالة داخلية، ما يجعل الشكل القابل للإنشاء. HelloContent
هو مثال على حالة قابلة للإنشاء
لأنّها تحتفظ بحالة name
الخاصة بها وتعدِّلها داخليًا. ويمكن أن يكون ذلك مفيدًا في الحالات التي لا يحتاج فيها المتصل إلى التحكّم في الحالة ويمكنه استخدامه بدون الحاجة إلى إدارة الحالة بنفسه. ومع ذلك، تميل المواد القابلة للإنشاء ذات الحالة الداخلية إلى أن تكون أقل قابلية لإعادة الاستخدام ويصعب اختبارها.
عنصر بدون حالة قابل للإنشاء هو عنصر قابل للإنشاء لا يحمل أي حالة. هناك طريقة سهلة لإلغاء الحالة هي استخدام رفع الدول.
أثناء تطوير عناصر قابلة لإعادة الاستخدام، غالبًا ما تحتاج إلى عرض إصدار ذي حالة ونسخة عديمة الحالة من العنصر نفسه القابل للإنشاء. يُعد إصدار حالة الحالة مناسبًا للمتصلين الذين لا يهتمون بحالتهم، كما أن الإصدار الذي لا يتضمّن حالة ضروري للمتصلين الذين يحتاجون إلى التحكم في الحالة أو رفعها.
الرفع إلى الولاية
إن رفع الحالة في Compose هو نمط من الانتقال إلى متصل ضِمن المحتوى القابل للإنشاء لجعله غير مرتبط بالحالة. النمط العام لرفع الحالة في Jetpack Compose هو استبدال متغير الحالة بمعلمتين:
value: T
: القيمة الحالية المطلوب عرضهاonValueChange: (T) -> Unit
: حدث يطلب تغيير القيمة، حيث تكونT
هي القيمة الجديدة المقترَحة
ومع ذلك، فأنت غير مقيد بـ onValueChange
. إذا كانت الأحداث الأكثر تحديدًا مناسبة للعنصر القابل للإنشاء،
يجب عليك تحديدها باستخدام lambdas.
للولاية التي تم رفعها بهذه الطريقة بعض الخصائص المهمة:
- مصدر واحد للحقيقة: من خلال تغيير الحالة بدلاً من تكرارها، نضمن توفُّر مصدر واحد فقط للحقيقة. يساعد هذا في تجنب الأخطاء.
- مغلف: يمكن فقط للعناصر القابلة للإنشاء التي لها حالة إنشاء تعديل حالتها. إنه داخلي تمامًا.
- قابلة للمشاركة: يمكن مشاركة حالة الرفع مع عدّة عناصر قابلة للإنشاء. إذا أردت قراءة
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
. وباتّباع تدفق البيانات أحادي الاتجاه، يمكنك فصل العناصر القابلة للإنشاء التي تعرِض الحالة في واجهة المستخدِم عن أجزاء تطبيقك التي تخزِّن وتغيّر الحالة.
اطّلِع على صفحة مكان الرفع لمعرفة المزيد من المعلومات.
استعادة الحالة في Compose
تعمل واجهة برمجة تطبيقات 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")) } }
عناصر الحالة في Compose
يمكن إدارة عملية رفع الحالة البسيطة في الدوال القابلة للإنشاء نفسها. ومع ذلك، إذا ظهر مقدار الحالة التي يجب استخدامها لتتبُّع الزيادات، أو ظهر المنطق المطلوب الأداء في الدوال القابلة للإنشاء، يكون تفويض مسؤوليات المنطق والدولة إلى فئات أخرى: أصحاب الحالات.
لمعرفة المزيد من المعلومات، يمكنك الاطّلاع على حالة إثبات الحالة في Compose أو صفحة عناصر الحالة وحالة واجهة المستخدم بشكل عام في دليل البنية.
إعادة تفعيل العمليات الحسابية لتذكُّر البيانات عند تغيير المفاتيح
يتم استخدام واجهة برمجة التطبيقات remember
بشكل متكرر مع MutableState
:
var name by remember { mutableStateOf("") }
هنا، يؤدي استخدام الدالة remember
إلى جعل القيمة MutableState
تظلّ صالحة عند إعادة التركيب.
بشكل عام، تأخذ remember
مَعلمة lambda calculation
. عند تشغيل remember
لأول مرة، تستدعي دالة lambda calculation
وتخزِّن نتيجتها. أثناء إعادة التركيب، تعرض remember
آخر قيمة تم تخزينها.
بخلاف حالة التخزين المؤقت، يمكنك أيضًا استخدام remember
لتخزين أي عنصر أو
نتيجة لعملية في المقطوعة الموسيقية يكون إعدادها أو
احتسابها مكلفًا. قد لا ترغب في تكرار هذه العملية الحسابية في كل إعادة التركيب.
مثال على إنشاء هذا الكائن ShaderBrush
، وهي عملية
مكلفة:
val brush = remember { ShaderBrush( BitmapShader( ImageBitmap.imageResource(res, avatarRes).asAndroidBitmap(), Shader.TileMode.REPEAT, Shader.TileMode.REPEAT ) ) }
تخزّن remember
القيمة إلى أن تتم إزالة المقطوعة الموسيقية. ومع ذلك، هناك طريقة
لإلغاء صلاحية القيمة المخزّنة مؤقتًا. تستخدم واجهة برمجة التطبيقات remember
أيضًا المعلمة key
أو keys
. إذا تغيّر أيّ من هذه المفاتيح، فإنّه في المرة القادمة التي تتم فيها إعادة إنشاء الدالة، يلغي remember
ذاكرة التخزين المؤقت وينفّذ عملية احتساب كتلة lambda مرة أخرى. تمنحك هذه الآلية إمكانية التحكم في مدة بقاء
كائن في المقطوعة الموسيقية. وتظل العملية الحسابية صالحة إلى أن تتغير الإدخالات، بدلاً من أن تغادر القيمة التي تم تذكرها المقطوعة الموسيقية.
توضّح الأمثلة التالية آلية عمل هذه الآلية.
في هذا المقتطف، يتم إنشاء ShaderBrush
واستخدامه كطلاء
خلفية لعنصر Box
قابل للإنشاء. تخزِّن remember
مثيل ShaderBrush
لأن إعادة إنشائها تكون مكلفة، كما أوضحنا سابقًا. يتم استخدام avatarRes
كمَعلمة key1
في remember
، وهي صورة الخلفية المحدّدة. في حال تغيير
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
. إن عرض هذه الدوال لإنشاء مثيل يحافظ على إعادة التركيب هو نمط شائع في 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 عملية تنفيذ الفئة يساوي للفئة لتحديد ما إذا كان المفتاح قد غيّر القيمة المخزّنة أم لا.
تخزين حالة المفاتيح باستخدام مفاتيح تتجاوز إعادة التركيب
واجهة برمجة التطبيقات rememberSaveable
هي برنامج تضمين حول remember
ويمكنه تخزين البيانات في Bundle
. وتتيح واجهة برمجة التطبيقات هذه للدولة البقاء على قيد الحياة ليس فقط عند إعادة التركيب، ولكنها تتيح أيضًا إنشاء النشاط والتخلّص من العملية التي يبدأها النظام.
تتلقّى rememberSaveable
مَعلمات input
للغرض نفسه الذي
تتلقّىه remember
keys
. يتم إيقاف ذاكرة التخزين المؤقت عند تغيير أي من الإدخالات. في المرة التالية التي تتم فيها إعادة إنشاء الدالة، تُعيد rememberSaveable
تنفيذ كتلة lambda الحسابية.
في المثال التالي، تخزّن rememberSaveable
السمة userTypedQuery
إلى أن يتم تغيير قيمة typedQuery
:
var userTypedQuery by rememberSaveable(typedQuery, stateSaver = TextFieldValue.Saver) { mutableStateOf( TextFieldValue(text = typedQuery, selection = TextRange(typedQuery.length)) ) }
مزيد من المعلومات
لمعرفة المزيد حول الحالة وJetpack Compose، يمكنك الاطّلاع على المراجع الإضافية التالية.
العيّنات
الدروس التطبيقية حول الترميز
الفيديوهات الطويلة
المدوّنات
أفلام مُقترَحة لك
- ملاحظة: يظهر نص الرابط عند إيقاف JavaScript
- تصميم واجهة مستخدم Compose
- حفظ حالة واجهة المستخدم في Compose
- الآثار الجانبية في Compose