खारिज करने या अपडेट करने के लिए स्वाइप करें

SwipeToDismissBox कॉम्पोनेंट की मदद से, उपयोगकर्ता किसी आइटम को बाईं या दाईं ओर स्वाइप करके उसे खारिज या अपडेट कर सकता है.

एपीआई का प्लैटफ़ॉर्म

स्वाइप जेस्चर से ट्रिगर होने वाली कार्रवाइयां लागू करने के लिए, 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, कॉम्पोनेंट की स्थिति को मैनेज करता है. आइटम के साथ इंटरैक्शन होने के बाद, यह confirmValueChange कॉलबैक को ट्रिगर करता है. कॉलबैक बॉडी, अलग-अलग संभावित कार्रवाइयों को मैनेज करता है. कॉलबैक एक बोलियन वैल्यू दिखाता है. इससे कॉम्पोनेंट को पता चलता है कि उसे खारिज करने वाला ऐनिमेशन दिखाना है या नहीं. इस मामले में:
    • अगर आइटम को शुरू से आखिर तक स्वाइप किया जाता है, तो यह onToggleDone लैम्ब्डा को कॉल करता है और मौजूदा todoItem को पास करता है. यह, 'क्या-क्या करें' आइटम को अपडेट करने से जुड़ा है.
    • अगर आइटम को आखिर से शुरू तक स्वाइप किया जाता है, तो यह onRemove lambda को कॉल करता है और मौजूदा todoItem को पास करता है. यह, 'क्या-क्या करें' सूची में मौजूद आइटम को मिटाने के बराबर है.
    • it != StartToEnd: अगर स्वाइप की दिशा StartToEnd नहीं है, तो यह लाइन true दिखाती है. अगर दिशा StartToEnd है, तो यह false दिखाती है. false को वापस लाने से, "टॉगल हो गया" स्वाइप करने के बाद, SwipeToDismissBox तुरंत नहीं दिखता. इससे विज़ुअल की पुष्टि करने या ऐनिमेशन दिखाने में मदद मिलती है.
  • 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 -> ... } एक कॉलबैक फ़ंक्शन है, जिसे TodoListItem में से शुरू किया जाता है. ऐसा तब होता है, जब उपयोगकर्ता किसी ऑब्जेक्ट को 'हो गया' के तौर पर मार्क करता है. यह todoItem की isItemDone प्रॉपर्टी को अपडेट करता है. todoItems एक mutableStateListOf है, इसलिए यह बदलाव यूज़र इंटरफ़ेस (यूआई) को अपडेट करते हुए, फिर से कॉम्पोज़ करने की प्रोसेस को ट्रिगर करता है.
  • onRemove = { todoItem -> ... } एक कॉलबैक फ़ंक्शन है, जो उपयोगकर्ता के आइटम हटाने पर ट्रिगर होता है. इससे todoItems सूची से कोई todoItem हट जाता है. इससे, फिर से कॉम्पोज़िशन भी होता है और आइटम को दिखाई गई सूची से हटा दिया जाता है.
  • हर TodoListItem पर animateItem मॉडिफ़ायर लागू किया जाता है, ताकि आइटम खारिज होने पर मॉडिफ़ायर का 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, Icon composable के कॉन्टेंट के पीछे सीधे कैनवस में ड्रॉ करता है.
    • 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 की सोर्स फ़ाइल देखें.

अन्य संसाधन