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

يسمح المكوّن 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 callback بعد الانتهاء من التفاعل مع العنصر. يعالج ملف الاستدعاء الإجراءات المختلفة المحتملة. يعرض الإجراء المُعاد برمجيًا قيمة برمجية تشكل تعبيرًا منطقيًا تحدّد للمكوّن ما إذا كان يجب عرض رسوم متحركة لإغلاق التطبيق. في هذه الحالة:
    • إذا تم التمرير سريعًا للعنصر من البداية إلى النهاية، يتم استدعاء دالة onToggleDone لامدا مع تمرير todoItem الحالية. ويتوافق ذلك مع تعديل مادة المهام.
    • إذا تم التمرير سريعًا للعنصر من النهاية إلى البداية، يتمّ استدعاء دالة onRemove lambda، مع تمرير 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
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 عناصر عند إضافة عنصر أو إزالته من هذه القائمة، يعيد تطبيق "الإنشاء" تركيب أجزاء واجهة المستخدم التي تعتمد عليه.
    • داخل 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(
                        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 المكوّن.
    • ترسم دالة 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 للحصول على نموذج الرمز البرمجي الكامل.

مراجع إضافية