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

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(
                        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, कॉम्पोनेंट की स्थिति को मैनेज करता है. आइटम के साथ इंटरैक्शन पूरा होने के बाद, यह confirmValueChange कॉलबैक को ट्रिगर करता है. कॉलबैक बॉडी, अलग-अलग संभावित कार्रवाइयों को मैनेज करती है. कॉलबैक, बूलियन वैल्यू दिखाता है. इससे कॉम्पोनेंट को पता चलता है कि उसे खारिज करने वाला ऐनिमेशन दिखाना चाहिए या नहीं. इस मामले में:
    • अगर आइटम को शुरू से आखिर तक स्वाइप किया जाता है, तो यह onToggleDone लैम्ब्डा को कॉल करता है और मौजूदा todoItem को पास करता है. यह टास्क की सूची में मौजूद आइटम को अपडेट करने से जुड़ा है.
    • अगर आइटम को आखिर से शुरू की ओर स्वाइप किया जाता है, तो यह onRemove लैम्ब्डा को कॉल करता है और मौजूदा todoItem को पास करता है. इसका मतलब है कि टास्क मिटा दिया गया है.
    • it != StartToEnd: अगर स्वाइप करने की दिशा StartToEnd नहीं है, तो यह लाइन true दिखाती है. अगर ऐसा नहीं है, तो यह false दिखाती है. false को वापस करने से, "टॉगल हो गया" स्वाइप के बाद SwipeToDismissBox तुरंत गायब नहीं होता. इससे विज़ुअल की पुष्टि या ऐनिमेशन की अनुमति मिलती है.
  • SwipeToDismissBox, हर आइटम पर हॉरिज़ॉन्टल स्वाइप करने की सुविधा देता है. बाकी समय में, यह कॉम्पोनेंट का अंदरूनी कॉन्टेंट दिखाता है. हालांकि, जब कोई उपयोगकर्ता स्वाइप करना शुरू करता है, तो कॉन्टेंट हट जाता है और backgroundContent दिखता है. सामान्य कॉन्टेंट और backgroundContent, दोनों को पैरंट कंटेनर की सभी पाबंदियां मिलती हैं, ताकि वे खुद को रेंडर कर सकें. content को backgroundContent के ऊपर ड्रॉ किया जाता है. इस मामले में:
    • backgroundContent को Icon के तौर पर लागू किया गया है. इसका बैकग्राउंड कलर, SwipeToDismissBoxValue पर आधारित है:
    • Blue जब StartToEnd स्वाइप किया जाता है, तो टास्क की सूची में मौजूद किसी आइटम को टॉगल किया जाता है.
    • EndToStart को स्वाइप करने पर Red — टास्क मिटाया जा रहा है.
    • 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 ऑब्जेक्ट को उनके ब्यौरे के साथ शुरू किया जाता है: "बिल भरें", "किराने का सामान खरीदें", "जिम जाएं", और "डिनर करें".
  • 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(
                        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, 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 सोर्स फ़ाइल देखें.

अन्य संसाधन