הרכיב SwipeToDismissBox מאפשר למשתמשים לסגור או לעדכן פריט על ידי החלקה שלו שמאלה או ימינה.
פלטפורמת ה-API
משתמשים בפונקציה הקומפוזבילית SwipeToDismissBox כדי להטמיע פעולות שמופעלות על ידי תנועות החלקה. הפרמטרים העיקריים כוללים:
-
state: המצבSwipeToDismissBoxStateשנוצר כדי לאחסן את הערך שנוצר על ידי חישובים בפריט ההחלקה, שמפעיל אירועים כשהוא נוצר. -
backgroundContent: תוכן קומפוזבילי שניתן להתאמה אישית ומוצג מאחורי תוכן הפריט, שנחשף כשמחליקים את התוכן.
דוגמה בסיסית: עדכון או סגירה בהחלקה
קטעי הקוד בדוגמה הזו מראים הטמעה של החלקה שמעדכנת את הפריט כשמחליקים מההתחלה לסוף, או סוגרת את הפריט כשמחליקים מהסוף להתחלה.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItem( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( painter = rememberVectorPainter(if (todoItem.isItemDone) AppIcons.CheckBox else AppIcons.CheckBoxOutlineBlank), contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .background(Color.Blue) .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( painter = rememberVectorPainter(AppIcons.Delete), contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(Color.Red) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } }
מידע חשוב על הקוד
-
swipeToDismissBoxStateקובע את מצב הרכיב. הוא מפעיל את הקריאה החוזרת (callback) שלconfirmValueChangeאחרי שהאינטראקציה עם הפריט מסתיימת. גוף הקריאה החוזרת (callback) מטפל בפעולות האפשריות השונות. הקריאה החוזרת מחזירה ערך בוליאני שמציין אם הרכיב צריך להציג אנימציה של סגירה. במקרה הזה:- אם מחליקים על הפריט מההתחלה עד הסוף, הוא קורא ל-lambda של
onToggleDoneומעביר את ה-todoItemהנוכחי. הפעולה הזו תואמת לעדכון של הפריט ברשימת המשימות. - אם מחליקים את הפריט מהסוף להתחלה, הפונקציה מפעילה את פונקציית הלמבדה
onRemoveומעבירה את ה-todoItemהנוכחי. הפעולה הזו תואמת למחיקת הפריט ברשימת המשימות. it != StartToEnd: השורה הזו מחזירה את הערךtrueאם כיוון ההחלקה הוא לאStartToEnd, ואת הערךfalseאם הוא כן. החזרת הערךfalseמונעת מה-SwipeToDismissBoxלהיעלם מיד אחרי החלקה לסימון "בוצע", ומאפשרת להציג אישור חזותי או אנימציה.
- אם מחליקים על הפריט מההתחלה עד הסוף, הוא קורא ל-lambda של
SwipeToDismissBoxמאפשר אינטראקציות של החלקה אופקית בכל פריט. במצב מנוחה, מוצג התוכן הפנימי של הרכיב, אבל כשהמשתמש מתחיל להחליק, התוכן זז ומופיעbackgroundContent. גם התוכן הרגיל וגםbackgroundContentמקבלים את האילוצים המלאים של מאגר ההורה כדי לעבד את עצמם. ה-contentמשורטט מעל ה-backgroundContent. במקרה הזה:-
backgroundContentמוטמע בתורIconעם צבע רקע שמבוסס עלSwipeToDismissBoxValue: BlueכשמחליקיםStartToEnd– החלפת מצב של משימה.RedכשמחליקיםEndToStart– מחיקת פריט ברשימת המשימות.- לא מוצג שום דבר ברקע של
Settled– כשלא מחליקים את הפריט, לא מוצג שום דבר ברקע. - באופן דומה, ה
Iconשמוצג משתנה בהתאם לכיוון ההחלקה: - ב-
StartToEnd, הסמלCheckBoxמופיע כשהמשימה הושלמה, והסמלCheckBoxOutlineBlankמופיע כשהיא לא הושלמה. - ב-
EndToStartמוצג הסמלDelete.
-
@Composable fun SwipeItemExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItem( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
מידע חשוב על הקוד
-
mutableStateListOf(...)יוצרת רשימה ניתנת לצפייה שיכולה להכיל אובייקטים מסוגTodoItem. כשמוסיפים פריט לרשימה הזו או מסירים ממנה פריט, Compose יוצר מחדש את חלקי ממשק המשתמש שתלויים בה.- בתוך
mutableStateListOf(), ארבעה אובייקטים מסוגTodoItemמאותחלים עם התיאורים שלהם: "Pay bills", "Buy groceries", "Go to gym" ו-"Get dinner".
- בתוך
-
LazyColumnמציג רשימה שלtodoItemsעם גלילה אנכית. -
onToggleDone = { todoItem -> ... }היא פונקציית קריאה חוזרת שמופעלת מתוךTodoListItemכשהמשתמש מסמן אובייקט כ"בוצע". הוא מעדכן את המאפייןisItemDoneשלtodoItem. מכיוון ש-todoItemsהואmutableStateListOf, השינוי הזה מפעיל קומפוזיציה מחדש ומעדכן את ממשק המשתמש. -
onRemove = { todoItem -> ... }היא פונקציית קריאה חוזרת שמופעלת כשהמשתמש מסיר את הפריט. היא מסירה את ה-todoItemהספציפי מהרשימהtodoItems. זה גם גורם ליצירה מחדש, והפריט יוסר מהרשימה שמוצגת. - משתנה מסוג
animateItemמוחל על כלTodoListItem, כך שהפונקציהplacementSpecשל המשתנה נקראת כשהפריט נסגר. הפעולה הזו מפעילה אנימציה של הסרת הפריט, וגם של שינוי הסדר של פריטים אחרים ברשימה.
תוצאה
בסרטון הבא מוצגת הפונקציונליות הבסיסית של החלקה לביטול מקטעי הקוד הקודמים:
הקוד המלא לדוגמה מופיע בקובץ המקור ב-GitHub.
דוגמה מתקדמת: אנימציה של צבע הרקע בהחלקה
קטעי הקוד הבאים מראים איך לשלב סף מיקום כדי להוסיף אנימציה לצבע הרקע של פריט בהחלקה.
data class TodoItem( val itemDescription: String, var isItemDone: Boolean = false )
@Composable fun TodoListItemWithAnimation( todoItem: TodoItem, onToggleDone: (TodoItem) -> Unit, onRemove: (TodoItem) -> Unit, modifier: Modifier = Modifier, ) { val swipeToDismissBoxState = rememberSwipeToDismissBoxState( confirmValueChange = { if (it == StartToEnd) onToggleDone(todoItem) else if (it == EndToStart) onRemove(todoItem) // Reset item when toggling done status it != StartToEnd } ) SwipeToDismissBox( state = swipeToDismissBoxState, modifier = modifier.fillMaxSize(), backgroundContent = { when (swipeToDismissBoxState.dismissDirection) { StartToEnd -> { Icon( painter = rememberVectorPainter(if (todoItem.isItemDone) AppIcons.CheckBox else AppIcons.CheckBoxOutlineBlank), contentDescription = if (todoItem.isItemDone) "Done" else "Not done", modifier = Modifier .fillMaxSize() .drawBehind { drawRect(lerp(Color.LightGray, Color.Blue, swipeToDismissBoxState.progress)) } .wrapContentSize(Alignment.CenterStart) .padding(12.dp), tint = Color.White ) } EndToStart -> { Icon( painter = rememberVectorPainter(AppIcons.Delete), contentDescription = "Remove item", modifier = Modifier .fillMaxSize() .background(lerp(Color.LightGray, Color.Red, swipeToDismissBoxState.progress)) .wrapContentSize(Alignment.CenterEnd) .padding(12.dp), tint = Color.White ) } Settled -> {} } } ) { OutlinedCard(shape = RectangleShape) { ListItem( headlineContent = { Text(todoItem.itemDescription) }, supportingContent = { Text("swipe me to update or remove.") } ) } } }
מידע חשוב על הקוד
drawBehindמצייר ישירות בלוח הציור מאחורי התוכן של ה-composableIcon.-
drawRect()מצייר מלבן על לוח הציור וממלא את כל הגבולות של היקף הציור ב-Colorשצוין.
-
- כשמחליקים, צבע הרקע של הפריט משתנה בצורה חלקה באמצעות
lerp.- כשמחליקים מ-
StartToEnd, צבע הרקע משתנה בהדרגה מאפור בהיר לכחול. - כשמחליקים מ-
EndToStart, צבע הרקע משתנה בהדרגה מאפור בהיר לאדום. - הערך של
swipeToDismissBoxState.progressקובע את מידת המעבר מצבע אחד לצבע הבא.
- כשמחליקים מ-
-
OutlinedCardמוסיף הפרדה חזותית עדינה בין הפריטים ברשימה.
@Composable private fun SwipeItemWithAnimationExample() { val todoItems = remember { mutableStateListOf( TodoItem("Pay bills"), TodoItem("Buy groceries"), TodoItem("Go to gym"), TodoItem("Get dinner") ) } LazyColumn { items( items = todoItems, key = { it.itemDescription } ) { todoItem -> TodoListItemWithAnimation( todoItem = todoItem, onToggleDone = { todoItem -> todoItem.isItemDone = !todoItem.isItemDone }, onRemove = { todoItem -> todoItems -= todoItem }, modifier = Modifier.animateItem() ) } } }
מידע חשוב על הקוד
- בקטע מידע חשוב שלמעלה מופיע מידע חשוב על הקוד, שמתאר קטע קוד זהה.
תוצאה
בסרטון הבא מוצגת הפונקציונליות המתקדמת עם צבע רקע מונפש:
הקוד המלא לדוגמה מופיע בקובץ המקור ב-GitHub.