אי אפשר לשנות את ממשק המשתמש בקטע 'פיתוח' – אי אפשר לעדכן אותו אחרי שהוא כבר
וצייר. מה שאפשר לשלוט בו הוא מצב ממשק המשתמש. בכל פעם שמצב
השינויים בממשק המשתמש, 'כתיבה' יוצר מחדש את החלקים של עץ ממשק המשתמש אשר
שונה. תכנים קומפוזביליים יכולים לקבל
במצב ולחשוף אירועים. לדוגמה, TextField
מקבל ערך וחושף
קריאה חוזרת (callback) onValueChange
שמבקשת את ה-handler של הקריאה החוזרת לשנות את
עם ערך מסוים.
var name by remember { mutableStateOf("") } OutlinedTextField( value = name, onValueChange = { name = it }, label = { Text("Name") } )
בגלל שחומרים קומפוזביליים מקבלים אירועים וחושפים אותם, זרימת הנתונים החד-כיוונית שהדפוס הזה מתאים היטב ל-Jetpack פיתוח נייטיב. המדריך הזה מתמקד באופן ההטמעה את הדפוס החד-כיווני של זרימת נתונים בכתיבה, כיצד להטמיע אירועים שלהם, ואיך לעבוד עם ViewModels בכתיבה.
זרימת נתונים חד-כיוונית
זרימת נתונים חד-כיוונית (UDF) היא דפוס עיצוב שבו המצב עובר כלפי מטה. והאירועים יתחילו לזרום. בעזרת זרימת נתונים חד-כיוונית אפשר לנתק תכנים קומפוזביליים שמציגים את המצב בממשק המשתמש מחלקי האפליקציה שמאחסנים ולשנות את המצב.
לולאת העדכון של ממשק המשתמש של אפליקציה שמשתמשת בזרם נתונים חד-כיווני נראית כך:
- אירוע: חלק מממשק המשתמש יוצר אירוע ומעביר אותו למעלה, למשל הלחיצה על הלחצן מועברת אל ViewModel לצורך טיפול; או שאירוע מועבר או שכבות אחרות של האפליקציה, כמו ציון שהסשן של המשתמש פג תוקף.
- עדכון מצב: הגורם המטפל באירועים עשוי לשנות את המצב.
- Display state (מצב התצוגה): בעל המדינה מעביר את המדינה (State), וממשק המשתמש מוצג את זה.
![](https://developer.android.com/static/develop/ui/compose/images/state-unidirectional-flow.png?authuser=8&hl=he)
יש כמה יתרונות לשימוש ב-Jetpack פיתוח נייטיב בהתאם לדפוס הזה:
- יכולת בדיקה: ניתוק מצב ממשק המשתמש מממשק המשתמש כדי לפשט את התהליך כדי לבדוק את שניהם בנפרד.
- הסתרת המדינה: אפשר לעדכן את המצב במקום אחד בלבד יש רק מקור אמת אחד למצב של תוכן קומפוזבילי, שסביר להניח שייווצרו באגים בגלל מצבים לא עקביים.
- עקביות בממשק המשתמש: כל עדכוני המצבים משתקפים מיד בממשק המשתמש על ידי
שימוש בבעלי מצבים גלויים, כמו
StateFlow
אוLiveData
.
תהליך חד-כיווני של נתונים ב-Jetpack פיתוח נייטיב
תכנים קומפוזביליים פועלים על סמך מצב ואירועים. לדוגמה, TextField
הוא רק
מתעדכן כשפרמטר value
מתעדכן והוא חושף onValueChange
קריאה חוזרת (callback) — אירוע שמבקש לשנות את הערך לערך חדש. אימייל חדש
מגדיר את האובייקט State
כמחזיק ערך, ומשנה את ערך המצב (State)
יובילו להרכבת מחדש. אפשר לשמור את המדינה
remember { mutableStateOf(value) }
או
rememberSaveable { mutableStateOf(value)
, בהתאם למשך הזמן הרצוי
תזכור את הערך של
סוג הערך של התוכן הקומפוזבילי TextField
הוא String
, לכן יכול להיות
מכל מקום - מערך שרשום בתוך הקוד, מ-ViewModel או שמועבר
תוכן קומפוזבילי הורה. לא חובה להחזיק אותו באובייקט State
, אבל צריך
כדי לעדכן את הערך כשמתבצעת קריאה אל onValueChange
.
הגדרת פרמטרים קומפוזביליים
כשמגדירים את הפרמטרים המצב של תוכן קומפוזבילי, צריך לשמור את הדברים הבאים לשאלות:
- עד כמה התוכן הקומפוזבילי הוא שימוש חוזר או גמיש?
- איך הפרמטרים של המצב משפיעים על הביצועים של התוכן הקומפוזבילי?
כדי לעודד פענוח קוד ושימוש חוזר, כל תוכן קומפוזבילי צריך להכיל את הכמות הקטנה ביותר של מידע אפשרי. לדוגמה, כשיוצרים תוכן קומפוזבילי שכולל את בכותרת של מאמר חדשותי, עדיף להעביר רק את המידע שצריך להיות מוצגת, ולא את המאמר החדשותי כולו:
@Composable fun Header(title: String, subtitle: String) { // Recomposes when title or subtitle have changed. } @Composable fun Header(news: News) { // Recomposes when a new instance of News is passed in. }
לפעמים, השימוש בפרמטרים בודדים גם משפר את הביצועים.
News
מכיל יותר מידע מאשר title
ו-subtitle
, בכל פעם
המופע החדש של News
מועבר אל Header(news)
, התוכן הקומפוזבילי
לכתוב מחדש, גם אם title
ו-subtitle
לא השתנו.
חשוב לשקול בקפידה את מספר הפרמטרים שאתם מעבירים. שימוש בפונקציה עם יותר מדי פרמטרים מקטינים את הארכיטקטורה של הפונקציה, כך שבמקרה הזה עדיף לקבץ אותם בכיתה.
אירועים בכתיבה
כל קלט לאפליקציה שלכם צריך להיות מיוצג כאירוע: הקשות, שינויי טקסט,
ואפילו מגבלות זמן או עדכונים אחרים. כשהאירועים האלה משנים את המצב של ממשק המשתמש,
ViewModel
צריך להיות זה שמטפל בהם ולעדכן את המצב של ממשק המשתמש.
שכבת ממשק המשתמש אף פעם לא אמורה לשנות את המצב מחוץ ל-handler של אירועים, כי עלולים ליצור חוסר עקביות ובאגים באפליקציה שלכם.
עדיף להעביר ערכים לא ניתנים לשינוי ל-lambdas של מצב ומטפל באירועים. הזה יש לה את היתרונות הבאים:
- שיפרנו את יכולת השימוש החוזר.
- מוודאים שממשק המשתמש לא משנה את ערך המדינה ישירות.
- ניתן להימנע מבעיות בו-זמניות (concurrency) כי מוודאים שהמדינה (State) שונה משרשור אחר.
- לרוב, מפחיתים את המורכבות של הקוד.
לדוגמה, תוכן קומפוזבילי שמקבל String
ו-lambda בתור פרמטרים
לקבל קריאה מהקשרים רבים, והוא ניתן לשימוש חוזר. נניח שהאפליקציה המובילה
בסרגל באפליקציה תמיד מציג טקסט ויש בו לחצן 'הקודם'. אפשר להגדיר
תוכן קומפוזבילי גנרי יותר של MyAppTopAppBar
שמקבל את הטקסט ומחזיר את הטקסט
הכינוי של הלחצן:
@Composable fun MyAppTopAppBar(topAppBarText: String, onBackPressed: () -> Unit) { TopAppBar( title = { Text( text = topAppBarText, textAlign = TextAlign.Center, modifier = Modifier .fillMaxSize() .wrapContentSize(Alignment.Center) ) }, navigationIcon = { IconButton(onClick = onBackPressed) { Icon( Icons.Filled.ArrowBack, contentDescription = localizedString ) } }, // ... ) }
הצגת מודלים, מצבים ואירועים: דוגמה
בעזרת ViewModel
ו-mutableStateOf
, אפשר גם להציג נתונים חד-כיווניים
באפליקציה שלך אם מתקיים אחד מהתנאים הבאים:
- המצב של ממשק המשתמש נחשף באמצעות בעלי מצבים גלויים, כמו
StateFlow
אוLiveData
. ViewModel
מטפל באירועים שמגיעים מממשק המשתמש או משכבות אחרות של האפליקציה ומעדכן את בעלי המדינה על סמך האירועים.
לדוגמה, כשמטמיעים מסך כניסה, מקישים על הלחצן כניסה. אמורה לגרום לאפליקציה להציג סימן גרפי שמוצג בזמן ההתקדמות ושיחת רשת. אם ההתחברות בוצעה בהצלחה, ואז האפליקציה עוברת למסך אחר. במקרה של שגיאה שהאפליקציה מציגה סרגל כלים. כך יוצרים מודל של מצב המסך והאירוע:
במסך יש ארבעה מצבים:
- יציאה: כשהמשתמש עדיין לא נכנס לחשבון.
- בתהליך: כשהאפליקציה מנסה עכשיו להכניס את המשתמש לחשבון עד ביצוע שיחת רשת.
- שגיאה: מתי אירעה שגיאה במהלך הכניסה.
- מחובר: כשהמשתמש מחובר לחשבון.
אפשר ליצור מודל של המצבים האלה כמחלקה חתומה. השדה ViewModel
חושף את המצב בתור
State
, מגדיר את המצב הראשוני ומעדכנת את המצב לפי הצורך.
ViewModel
גם מטפל באירוע הכניסה על ידי חשיפת method של onSignIn()
.
class MyViewModel : ViewModel() { private val _uiState = mutableStateOf<UiState>(UiState.SignedOut) val uiState: State<UiState> get() = _uiState // ... }
בנוסף ל-API של mutableStateOf
, 'כתיבה' מאפשרת
תוספים עבור LiveData
, Flow
, ו-
Observable
כדי להירשם כמאזינים ולייצג את הערך כמצב.
class MyViewModel : ViewModel() { private val _uiState = MutableLiveData<UiState>(UiState.SignedOut) val uiState: LiveData<UiState> get() = _uiState // ... } @Composable fun MyComposable(viewModel: MyViewModel) { val uiState = viewModel.uiState.observeAsState() // ... }
מידע נוסף
מידע נוסף על ארכיטקטורה ב-Jetpack פיתוח נייטיב זמין במקורות המידע הבאים:
דוגמיות
מומלץ עבורך
- הערה: טקסט הקישור מוצג כאשר JavaScript מושבת
- State ו-Jetpack פיתוח נייטיב
- שמירת המצב של ממשק המשתמש בחלונית הכתיבה
- טיפול בקלט של משתמשים