نادرًا ما تكون واجهات المستخدم الحديثة ثابتة. تتغيّر حالة واجهة المستخدم عندما يتفاعل المستخدم معها أو عندما يحتاج التطبيق إلى عرض بيانات جديدة.
تحدّد هذه المستندات إرشادات لإنتاج حالة واجهة المستخدم وإدارتها. تهدف هذه المستندات إلى مساعدتك في فهم ما يلي:
- واجهات برمجة التطبيقات التي يجب استخدامها لإنتاج حالة واجهة المستخدم: يعتمد ذلك على طبيعة مصادر تغيير الحالة المتوفّرة في عناصر الاحتفاظ بالحالة، وفقًا لمبادئ تدفق البيانات أحادي الاتجاه. يعتمد هذا على طبيعة مصادر تغيير الحالة المتاحة في حاملي الحالة لديك، باتباع مبادئ تدفق البيانات أحادي الاتجاه.
- كيفية تحديد نطاق إنتاج حالة واجهة المستخدم ليكون واعيًا بموارد النظام.
- بشكل أساسي، إنتاج الحالة هو تطبيق هذه التغييرات بشكل تدريجي على حالة واجهة المستخدم.
تكون الحالة موجودة دائمًا، وتتغيّر نتيجةً للأحداث. يتم تلخيص الاختلافات بين الأحداث والحالة في الجدول أدناه: الأحداث
| الحالة | الحالة |
|---|---|
| موجودة دائمًا | مدخلات إنتاج الحالة |
| ناتج إنتاج الحالة | ناتج الحالة |
| تستهلكها واجهة المستخدم | عبارة **الحالة هي؛ الأحداث تحدث** هي عبارة تذكّرية رائعة تلخّص ما سبق. |
يساعد الرسم البياني أدناه في تصوُّر التغييرات التي تطرأ على الحالة عند وقوع الأحداث في مخطط زمني. يساعد المخطّط البياني أدناه في تصور التغييرات التي تطرأ على الحالة مع وقوع الأحداث في المخطط الزمني.  **الشكل 1** : الأحداث تؤدي إلى تغيير الحالة
**المستخدمون** : أثناء تفاعلهم مع واجهة مستخدم التطبيق
- مصادر أخرى لتغيير الحالة: واجهات برمجة التطبيقات التي تعرض بيانات التطبيق من طبقات واجهة المستخدم أو النطاق أو البيانات، مثل أحداث مهلة Snackbar أو حالات الاستخدام أو المستودعات على التوالي
- مصادر أخرى لتغيير الحالة: واجهات برمجة التطبيقات التي تقدم بيانات التطبيق من واجهة المستخدم، أو طبقات المجال أو البيانات مثل أحداث مهلة شريط الوجبات الخفيفة، أو حالات الاستخدام، أو المستودعات، على التوالي.
يمكن اعتبار إنتاج الحالة في تطبيقات Android خط أنابيب معالجة يتألف من ما يلي:
يمكن اعتبار إنتاج الحالة في تطبيقات Android بمثابة خط أنابيب معالجة يتألف مما يلي:
- المدخلات: مصادر تغيير الحالة. محلية في طبقة واجهة المستخدم: قد تكون هذه الأحداث أحداثًا للمستخدم، مثل إدخال المستخدم عنوانًا لـ "مهمة" في تطبيق لإدارة المهام، أو واجهات برمجة التطبيقات التي تتيح الوصول إلى منطق واجهة المستخدم الذي يؤدي إلى تغييرات في حالة واجهة المستخدم، على سبيل المثال، استدعاء طريقة `open` على `DrawerState` في Jetpack Compose
- خارجية في طبقة واجهة المستخدم: هذه هي المصادر من طبقات النطاق أو البيانات التي تؤدي إلى تغييرات في حالة واجهة المستخدم، على سبيل المثال، الأخبار التي تم تحميلها من `NewsRepository` أو أحداث أخرى
openDrawerState - مزيج من ما سبق
NewsRepository - **عناصر الاحتفاظ بالحالة**: هي أنواع تطبّق منطق النشاط التجاري ومنطق واجهة المستخدم على مصادر تغيير الحالة، وتعالج أحداث المستخدم لإنتاج حالة واجهة المستخدم.
- خارجية في طبقة واجهة المستخدم: هذه هي المصادر من طبقات النطاق أو البيانات التي تؤدي إلى تغييرات في حالة واجهة المستخدم، على سبيل المثال، الأخبار التي تم تحميلها من `NewsRepository` أو أحداث أخرى
- حاملو الحالة: أنواع تطبق منطق الأعمال ومنطق واجهة المستخدم على مصادر تغيير الحالة، وتعالج أحداث المستخدم لإنتاج حالة واجهة المستخدم.
- الإخراج: حالة واجهة المستخدم التي يمكن للتطبيق عرضها لتزويد المستخدمين بالمعلومات التي يحتاجونها.
هناك واجهتا برمجة تطبيقات رئيسيتان تُستخدمان في إنتاج الحالة، وذلك حسب مرحلة خط الأنابيب التي تستخدمها:
مرحلة خط الأنابيب
| مرحلة مسار التعلّم | الإدخال |
|---|---|
| استخدِم واجهات برمجة التطبيقات غير المتزامنة، مثل الإجراءات البرمجية المتزامنة وFlows، لتنفيذ العمل خارج سلسلة التعليمات البرمجية لواجهة المستخدم من أجل الحفاظ على سلاسة واجهة المستخدم. | استخدِم واجهات برمجة التطبيقات غير المتزامنة، مثل Coroutines وFlows، لتنفيذ المهام خارج سلسلة واجهة المستخدم للحفاظ على واجهة المستخدم خالية من الإيقاف المؤقت لعرض واجهة المستخدم. |
| استخدِم واجهات برمجة التطبيقات لعناصر الاحتفاظ بالبيانات القابلة للمراقبة، مثل Compose State أو StateFlow، لإبطال واجهة المستخدم وإعادة عرضها عند تغيير الحالة. | استخدِم واجهات برمجة تطبيقات حاملي البيانات القابلة للتتبّع مثل Compose State أو StateFlow لـ إلغاء صلاحية واجهة المستخدم وإعادة عرضها عند تغيير الحالة. تضمن حامِلات البيانات القابلة للمراقبة أن واجهة المستخدم لديها دائمًا حالة واجهة مستخدم لعرضها على الشاشة. |
يؤثر اختيار واجهة برمجة التطبيقات غير المتزامنة للمدخلات بشكل أكبر على طبيعة مسار إنتاج الحالة من اختيار واجهة برمجة التطبيقات القابلة للملاحظة للمخرجات. تجميع خط أنابيب إنتاج الحالة
تتناول الأقسام التالية تقنيات إنتاج الحالة الأنسب لمختلف المدخلات وواجهات برمجة التطبيقات المطابقة للناتج.
كل خط أنابيب لإنتاج الحالة هو مزيج من المدخلات والمخرجات ويجب أن يكون على النحو التالي: يُعدّ كل خط أنابيب لإنتاج الحالة مزيجًا من المدخلات والمخرجات ويجب أن يكون على النحو التالي:
- مراحل النشاط: في الحالة التي لا تكون فيها واجهة المستخدم مرئية أو نشطة، يجب ألا يستهلك مسار إنتاج الحالة أي موارد ما لم يكن ذلك مطلوبًا بشكل صريح.
- سهولة الاستهلاك: يجب أن تكون واجهة المستخدم قادرة على عرض حالة واجهة المستخدم التي تم إنتاجها بسهولة. في Jetpack Compose، يُعد استهلاك الحالة أمرًا أساسيًا لواجهة المستخدم، لأن الدوال المركّبة يمكنها التحديث بناءً على تغييرات الحالة.
توفّر المدخلات في خط أنابيب إنتاج الحالة مصادر تغيير الحالة من خلال ما يلي:
المدخلات في مسار الإنتاج توفر مصادر تغيير الحالة من خلال ما يلي:
- العمليات أحادية اللقطة التي يمكن أن تكون متزامنة أو غير متزامنة—على سبيل المثال،
استدعاءات دوال
suspend - كل ما سبق
Flows - تتناول الأقسام التالية كيفية تجميع خط أنابيب لإنتاج الحالة لكل من المدخلات أعلاه.
تغطي الأقسام التالية كيفية تجميع مسار إنتاج للحالة لكل من المدخلات المذكورة أعلاه.
يمكنك إدارة الحالة باستخدام عناصر الاحتفاظ بالبيانات القابلة للمراقبة.
إدارة الحالة باستخدام حاملي البيانات القابلة للملاحظة. استخدِم واجهة برمجة التطبيقات mutableStateOf،
خاصةً عند العمل مع واجهات برمجة تطبيقات النصوص في Compose. لإدارة حالة أكثر تعقيدًا أو عند الدمج مع مكونات معمارية أخرى، استخدِم واجهة برمجة التطبيقات
MutableStateFlow. على سبيل المثال، ضع في اعتبارك تعديلات الحالة في تطبيق بسيط لرمي النرد. يؤدي كل رمية نرد من المستخدم إلى استدعاء طريقة `Random.nextInt` المتزامنة، ويتم تسجيل النتيجة في حالة واجهة المستخدم.
Compose StateRandom.nextInt
حالة Compose
@Stable
interface DiceUiState {
val firstDieValue: Int?
val secondDieValue: Int?
val numberOfRolls: Int?
}
private class MutableDiceUiState: DiceUiState {
override var firstDieValue: Int? by mutableStateOf(null)
override var secondDieValue: Int? by mutableStateOf(null)
override var numberOfRolls: Int by mutableStateOf(0)
}
class DiceRollViewModel : ViewModel() {
private val _uiState = MutableDiceUiState()
val uiState: DiceUiState = _uiState
// Called from the UI
fun rollDice() {
_uiState.firstDieValue = Random.nextInt(from = 1, until = 7)
_uiState.secondDieValue = Random.nextInt(from = 1, until = 7)
_uiState.numberOfRolls = _uiState.numberOfRolls + 1
}
}
تغيير حالة واجهة المستخدم من خلال طلبات غير متزامنة
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Called from the UI
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
بالنسبة إلى تغييرات الحالة التي تتطلّب نتيجة غير متزامنة، شغِّل إجراءً برمجية متزامنة في `CoroutineScope` المناسب.
بالنسبة لتغييرات الحالة التي تتطلب نتيجة غير متزامنة، قم بتشغيل كوروتين في CoroutineScope المناسب. بعد ذلك، يسجِّل عنصر الاحتفاظ بالحالة نتيجة استدعاء طريقة الإيقاف المؤقت في واجهة برمجة التطبيقات القابلة للمراقبة المستخدَمة لعرض حالة واجهة المستخدم.CoroutineScope يكتب عنصر الاحتفاظ بالحالة بعد ذلك نتيجة طلب إجراء التعليق في واجهة برمجة التطبيقات القابلة للملاحظة المستخدمة لعرض حالة واجهة المستخدم.
على سبيل المثال، ضع في اعتبارك AddEditTaskViewModel في
نموذج بنية التطبيق. Compose StatesaveTaskupdate
حالة Compose
@Stable
interface AddEditTaskUiState {
val title: String
val description: String
val isTaskCompleted: Boolean
val isLoading: Boolean
val userMessage: String?
val isTaskSaved: Boolean
}
private class MutableAddEditTaskUiState : AddEditTaskUiState() {
override var title: String by mutableStateOf("")
override var description: String by mutableStateOf("")
override var isTaskCompleted: Boolean by mutableStateOf(false)
override var isLoading: Boolean by mutableStateOf(false)
override var userMessage: String? by mutableStateOf<String?>(null)
override var isTaskSaved: Boolean by mutableStateOf(false)
}
class AddEditTaskViewModel(...) : ViewModel() {
private val _uiState = MutableAddEditTaskUiState()
val uiState: AddEditTaskUiState = _uiState
private fun createNewTask() {
viewModelScope.launch {
val newTask = Task(uiState.value.title, uiState.value.description)
try {
tasksRepository.saveTask(newTask)
// Write data into the UI state.
_uiState.isTaskSaved = true
}
catch(cancellationException: CancellationException) {
throw cancellationException
}
catch(exception: Exception) {
_uiState.userMessage = getErrorMessage(exception))
}
}
}
}
**ملاحظة:** يتم تشغيل الإجراءات البرمجية المتزامنة التي يتم تشغيلها في `viewModelScope` من `ViewModel` في AAC حتى اكتمالها، سواء كان ذلك بشكل استثنائي أو غير ذلك.
data class AddEditTaskUiState(
val title: String = "",
val description: String = "",
val isTaskCompleted: Boolean = false,
val isLoading: Boolean = false,
val userMessage: String? = null,
val isTaskSaved: Boolean = false
)
class AddEditTaskViewModel(...) : ViewModel() {
private val _uiState = MutableStateFlow(AddEditTaskUiState())
val uiState: StateFlow<AddEditTaskUiState> = _uiState.asStateFlow()
private fun createNewTask() {
viewModelScope.launch {
val newTask = Task(uiState.value.title, uiState.value.description)
try {
tasksRepository.saveTask(newTask)
// Write data into the UI state.
_uiState.update {
it.copy(isTaskSaved = true)
}
}
catch(cancellationException: CancellationException) {
throw cancellationException
}
catch(exception: Exception) {
_uiState.update {
it.copy(userMessage = getErrorMessage(exception))
}
}
}
}
}
من الأفضل تشغيل الإجراءات البرمجية المتزامنة على برنامج الإرسال الرئيسي لإنتاج حالة واجهة المستخدم، أي خارج كتلة `withContext` في مقتطفات الرموز البرمجية أدناه.
من الأفضل تشغيل Coroutines على المرسل الرئيسي لإنتاج
حالة واجهة المستخدم - أي خارج الكتلة withContext في مقتطفات التعليمات البرمجية أدناه.
استخدِم طريقة `withContext` لتشغيل الإجراءات البرمجية المتزامنة في سياق متزامن مختلف.
- استخدِم طريقة
withContextلتشغيل Coroutines في سياق متزامن مختلف. - عند استخدام
MutableStateFlow، استخدِم طريقةupdateكالمعتاد. - عند استخدام Compose State، استخدِم
Snapshot.withMutableSnapshotلضمان التحديثات الذرية للحالة في السياق المتزامن.
For example, assume that in the DiceRollViewModel snippet below,
SlowRandom.nextInt is a computationally intensive suspend function that
needs to be called from a CPU-bound Coroutine.
حالة Compose
class DiceRollViewModel(
private val defaultDispatcher: CoroutineScope = Dispatchers.Default
) : ViewModel() {
private val _uiState = MutableDiceUiState()
val uiState: DiceUiState = _uiState
// Called from the UI
fun rollDice() {
viewModelScope.launch() {
// Other Coroutines that may be called from the current context
…
withContext(defaultDispatcher) {
Snapshot.withMutableSnapshot {
_uiState.firstDieValue = SlowRandom.nextInt(from = 1, until = 7)
_uiState.secondDieValue = SlowRandom.nextInt(from = 1, until = 7)
_uiState.numberOfRolls = _uiState.numberOfRolls + 1
}
}
}
}
}
**ملاحظة:** إذا كان يجب استدعاء جميع الإجراءات البرمجية المتزامنة التي تم تشغيلها من سياق مختلف، يمكنك استدعاء `viewModelScope.launch(defaultDispatcher){ }` مباشرةً.**تحذير:** يمكن أن يؤدي تعديل حالة Compose من سلسلة تعليمات برمجية غير تابعة لواجهة المستخدم بدون استخدام `Snapshot.withMutableSnapshot{ }` إلى حدوث حالات غير متسقة في الحالة المنتَجة.
class DiceRollViewModel(
private val defaultDispatcher: CoroutineScope = Dispatchers.Default
) : ViewModel() {
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Called from the UI
fun rollDice() {
viewModelScope.launch() {
// Other Coroutines that may be called from the current context
…
withContext(defaultDispatcher) {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = SlowRandom.nextInt(from = 1, until = 7),
secondDieValue = SlowRandom.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
}
}
بالنسبة إلى مصادر تغيير الحالة التي تنتج قيمًا متعدّدة بمرور الوقت في البث، فإنّ تجميع نواتج جميع المصادر في وحدة متماسكة هو نهج بسيط لإنتاج الحالة.
عند استخدام Kotlin Flows، يمكنك تحقيق ذلك باستخدام دالة combine.
عند استخدام Kotlin Flows، يمكنك تحقيق ذلك باستخدام الدالة combine.
**ملاحظة:** يمكنك استخدام عامل التشغيل `stateIn` لتحويل `Flow` المجمّع إلى `StateFlow` كواجهة برمجة تطبيقات قابلة للمراقبة لحالة واجهة المستخدم. مثال على ذلك يمكن رؤيته في نموذج"الآن في Android" في الـ
InterestsViewModel:
class InterestsViewModel(
authorsRepository: AuthorsRepository,
topicsRepository: TopicsRepository
) : ViewModel() {
val uiState = combine(
authorsRepository.getAuthorsStream(),
topicsRepository.getTopicsStream(),
) { availableAuthors, availableTopics ->
InterestsUiState.Interests(
authors = availableAuthors,
topics = availableTopics
)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = InterestsUiState.Loading
)
}
استخدِم stateIn إذا كان يجب أن يكون خط الأنابيب نشطًا فقط عندما تكون واجهة المستخدم مرئية أثناء جمع البث بطريقة تراعي دورة الحياة.StateFlows
- استخدِم
SharingStarted.WhileSubscribedإذا كان يجب أن يكون خط الأنابيب نشطًا فقط عندما تكون واجهة المستخدم مرئية أثناء جمع التدفق بطريقة تراعي مراحل النشاط. - استخدِم
SharingStarted.Lazilyإذا كان المسار بحاجة إلى أن يكون نشطًا طالما أن المستخدم قد يعود إلى واجهة المستخدم، أي أن واجهة المستخدم في حزمة الخلفية أو في علامة تبويب أخرى خارج الشاشة.
في الحالات التي لا ينطبق فيها تجميع مصادر الحالة المستندة إلى البث، توفر واجهات برمجة تطبيقات البث مثل Kotlin Flows مجموعة غنية من التحويلات مثل الدمج، والتسوية، وما إلى ذلك للمساعدة في معالجة البث في حالة واجهة المستخدم.
في حال كان خط أنابيب إنتاج الحالة يعتمد على كل من عمليات الاستدعاء التي يتم تنفيذها مرة واحدة والبث كمصادر لتغيير الحالة، فإنّ البث هو القيد المحدد.
في الحالة التي يعتمد فيها مسار الإنتاج على كل من الاستدعاءات التي يتم تنفيذها مرة واحدة والبث كمصادر لتغيير الحالة، يكون البث هو الشرط المحدد. باستخدام التدفقات، يعني ذلك عادةً إنشاء واحد أو أكثر من مثيلات `MutableStateFlow` الخاصة بالدعم لتوزيع تغييرات الحالة.
باستخدام التدفقات، يعني هذا عادةً إنشاء مثيل واحد أو أكثر من مثيلات الدعم الخاصة
MutableStateFlow لنشر تغييرات الحالة. يمكنك أيضًا إنشاء
تدفقات لقطات من حالة Compose.
ضع في اعتبارك TaskDetailViewModel من مستودع architecture-samples. تعتمد حالة واجهة المستخدم على دفق للمهمة الحالية (_task) و
مصدر لمرة واحدة (_isTaskDeleted) يتم تحديثه عند حذف المهمة. Compose State
حالة Compose
class TaskDetailViewModel @Inject constructor(
private val tasksRepository: TasksRepository,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private var _isTaskDeleted by mutableStateOf(false)
private val _task = tasksRepository.getTaskStream(taskId)
val uiState: StateFlow<TaskDetailUiState> = combine(
snapshotFlow { _isTaskDeleted },
_task
) { isTaskDeleted, taskAsync ->
TaskDetailUiState(
task = taskAsync.data,
isTaskDeleted = isTaskDeleted
)
}
// Convert the result to the appropriate observable API for the UI
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = TaskDetailUiState()
)
fun deleteTask() = viewModelScope.launch {
tasksRepository.deleteTask(taskId)
_isTaskDeleted = true
}
}
**ملاحظة:** يتم تحويل `State` في Compose إلى تدفق باستخدام واجهة برمجة التطبيقات `snapshotFlow`.
class TaskDetailViewModel @Inject constructor(
private val tasksRepository: TasksRepository,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val _isTaskDeleted = MutableStateFlow(false)
private val _task = tasksRepository.getTaskStream(taskId)
val uiState: StateFlow<TaskDetailUiState> = combine(
_isTaskDeleted,
_task
) { isTaskDeleted, taskAsync ->
TaskDetailUiState(
task = taskAsync.data,
isTaskDeleted = isTaskDeleted
)
}
// Convert the result to the appropriate observable API for the UI
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = TaskDetailUiState()
)
fun deleteTask() = viewModelScope.launch {
tasksRepository.deleteTask(taskId)
_isTaskDeleted.update { true }
}
}
يعتمد اختيار واجهة برمجة تطبيقات الناتج لحالة واجهة المستخدم وطبيعة عرضها إلى حد كبير على واجهة برمجة التطبيقات التي يستخدمها تطبيقك لعرض واجهة المستخدم، مثل Compose.
Jetpack Compose هي مجموعة الأدوات الحديثة المقترَحة لإنشاء واجهة مستخدم محلية. Jetpack Compose هي مجموعة الأدوات الحديثة الموصى بها لإنشاء واجهة مستخدم أصلية. قراءة الحالة بطريقة تراعي دورة الحياة
- ما إذا كان سيتم عرض الحالة في حقل واحد أو حقول متعدّدة من عنصر الاحتفاظ بالحالة
- يلخّص الجدول التالي واجهات برمجة التطبيقات التي يجب استخدامها لخط أنابيب إنتاج الحالة عند استخدام Jetpack Compose:
يلخص الجدول التالي واجهات برمجة التطبيقات التي يجب استخدامها لمسار إنتاج الحالة عند استخدام Jetpack Compose:
| الناتج | واجهات برمجة التطبيقات التي يتم تنفيذها مرة واحدة |
|---|---|
| `StateFlow` أو `State` في Compose | StateFlow أو Compose State |
| واجهات برمجة التطبيقات التي يتم تنفيذها مرة واحدة وواجهات برمجة تطبيقات البث | StateFlow |
| تهيئة خط أنابيب إنتاج الحالة | StateFlow |
تتضمّن تهيئة خطوط أنابيب إنتاج الحالة ضبط الشروط الأولية لتشغيل خط الأنابيب.
يتضمن إعداد مسارات إنتاج الحالة تحديد الشروط الأولية لتشغيل المسار. عند الإمكان، يمكنك تهيئة خط أنابيب إنتاج الحالة بشكل غير مباشر للحفاظ على موارد النظام.id
عند الإمكان، قم بتهيئة مسار إنتاج الحالة بشكل كسول للحفاظ على موارد النظام. تسمح واجهات برمجة التطبيقات `Flow` بذلك باستخدام الوسيطة `started` في طريقة `stateIn`. تسمح واجهات برمجة التطبيقات Flow بذلك باستخدام الوسيطة started
في الطريقة stateIn. في الحالات التي لا ينطبق فيها ذلك، حدد
دالة متساوية القوى initialize لبدء مسار إنتاج الحالة
بشكل صريح كما هو موضح في المقتطف التالي:
class MyViewModel : ViewModel() {
private var initializeCalled = false
// This function is idempotent provided it is only called from the UI thread.
@MainThread
fun initialize() {
if(initializeCalled) return
initializeCalled = true
viewModelScope.launch {
// seed the state production pipeline
}
}
}
initViewModelIllegalStateException
توضّح نماذج Google التالية إنتاج الحالة في طبقة واجهة المستخدم.
توضح نماذج Google التالية إنتاج الحالة في طبقة واجهة المستخدم. مراجع إضافية
لمزيد من المعلومات عن حالة واجهة المستخدم، يُرجى الاطّلاع على المراجع الإضافية التالية:
الوثائق
الحالة وJetpack Compose
إنتاج حالة واجهة المستخدم (طريقة العرض)
مُقترَحة لك
- ملاحظة: يتم عرض نص الرابط عند إيقاف JavaScript
- إنشاء تطبيق يعمل بدون اتصال بالإنترنت أولاً
- عناصر الاحتفاظ بالحالة وحالة واجهة المستخدم {:#mad-arch}
- عناصر الاحتفاظ بالحالة وحالة واجهة المستخدم {:#mad-arch}