הרכיב SwipeToDismissBox
מאפשר למשתמש לסגור או לעדכן פריט על ידי החלקה ימינה או שמאלה.
ממשק API
אפשר להשתמש ב-composable 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( if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.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( imageVector = Icons.Default.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
מנהל את מצב הרכיב. הוא מפעיל את פונקציית ה-callbackconfirmValueChange
בסיום האינטראקציה עם הפריט. גוף הקריאה החוזרת מטפל בפעולות השונות האפשריות. הפונקציה הזו מחזירה ערך בוליאני שמציין אם צריך להציג אנימציה לסגירה. במקרה כזה:- אם מחליקים את הפריט מההתחלה לסוף, מתבצעת קריאה ל-lambda של
onToggleDone
, עם העברת הערך הנוכחי שלtodoItem
. זה תואם לעדכון של פריט המשימות. - אם מחליקים את הפריט מהסוף להתחלה, מתבצעת קריאה ל-lambda של
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 private 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
עם התיאורים המתאימים: 'תשלום חשבונות', 'קניית מצרכים', 'ביקור בחדר כושר' ו'ארוחת ערב'.
- בתוך
LazyColumn
מציג רשימה שלtodoItems
בגלילה אנכית.onToggleDone = { todoItem -> ... }
היא פונקציית קריאה חוזרת (callback) שמופיעה ב-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( if (todoItem.isItemDone) Icons.Default.CheckBox else Icons.Default.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( imageVector = Icons.Default.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
מצייר ישירות על לוח הציור מאחורי התוכן של ה-composable שלIcon
.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.