التمرير سريعًا لتجاهل الرسالة أو تعديلها

يتيح مكوّن SwipeToDismissBox للمستخدم إغلاق عنصر أو تعديله من خلال التمرير سريعًا إلى اليمين أو اليسار.

مساحة عرض واجهة برمجة التطبيقات

استخدِم الدالة المركّبة SwipeToDismissBox لتنفيذ الإجراءات التي يتم تفعيلها من خلال إيماءات التمرير السريع. تشمل المَعلمات الرئيسية ما يلي:

  • state: حالة SwipeToDismissBoxState التي تم إنشاؤها لتخزين القيمة الناتجة عن العمليات الحسابية على عنصر التمرير السريع، والتي تؤدي إلى تشغيل الأحداث عند إنتاجها.
  • ‫backgroundContent: دالة مركّبة قابلة للتخصيص يتم عرضها خلف محتوى العنصر الذي يظهر عند التمرير سريعًا على المحتوى

مثال أساسي: التعديل أو الإغلاق عند التمرير السريع

The snippets in this example show a swipe implementation that either updates the item when swiped from start to end, or dismisses the item when swiped from end to start.

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 بعد انتهاء التفاعل مع العنصر. يعالج نص رد الاتصال الإجراءات المختلفة الممكنة. تعرض دالة الاستدعاء قيمة منطقية تخبر المكوّن بما إذا كان يجب عرض تأثير متحرك للإغلاق. في هذه الحالة:
    • إذا تم تمرير العنصر من البداية إلى النهاية، يتم استدعاء دالة lambda onToggleDone، مع تمرير todoItem الحالي. يتوافق هذا مع تحديث عنصر قائمة المهام.
    • إذا تم تمرير العنصر من النهاية إلى البداية، يتم استدعاء دالة lambda onRemove، مع تمرير todoItem الحالي. يتوافق هذا مع حذف عنصر قائمة المهام.
    • ‫it != 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
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 عندما يضع المستخدم علامة على عنصر على أنّه مكتمل. يتم تعديل السمة isItemDone لـ todoItem. بما أنّ todoItems هو mutableStateListOf، يؤدي هذا التغيير إلى إعادة إنشاء واجهة المستخدم وتعديلها.
  • ‫onRemove = { todoItem -> ... } هي دالّة رد اتصال يتم تشغيلها عندما يزيل المستخدم العنصر. يؤدي ذلك إلى إزالة todoItem المحدّد من قائمة todoItems. يؤدي هذا أيضًا إلى إعادة تركيب، وستتم إزالة العنصر من القائمة المعروضة.
  • يتم تطبيق المُعدِّل animateItem على كل TodoListItem بحيث يتم استدعاء placementSpec للمُعدِّل عند إغلاق العنصر. يؤدي ذلك إلى إضافة تأثيرات حركية لإزالة العنصر، بالإضافة إلى إعادة ترتيب العناصر الأخرى في القائمة.

النتيجة

يوضّح الفيديو التالي وظيفة التمرير السريع الأساسية للإغلاق من المقتطفات السابقة:

الشكل 1. تنفيذ أساسي لميزة "التمرير السريع للإغلاق" التي يمكنها وضع علامة على عنصر على أنّه مكتمل وعرض صورة متحركة للإغلاق لعنصر في قائمة

يمكنك الاطّلاع على ملف مصدر 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()
            )
        }
    }
}

نقاط رئيسية حول الرمز

  • للاطّلاع على النقاط الرئيسية حول هذا الرمز، يُرجى مراجعة النقاط الرئيسية من قسم سابق يصف مقتطف رمز مطابقًا.

النتيجة

يعرض الفيديو التالي الوظيفة المتقدمة مع لون الخلفية المتحركة:

الشكل 2. مثال على تنفيذ ميزة التمرير السريع للكشف أو الحذف، مع ألوان خلفية متحركة وحدّ أطول قبل تسجيل الإجراء

يمكنك الاطّلاع على ملف مصدر GitHub للحصول على الرمز النموذجي الكامل.

مراجع إضافية