برای رد کردن یا به‌روزرسانی، انگشت خود را بکشید

مولفه SwipeToDismissBox به کاربر اجازه می دهد تا یک مورد را با کشیدن انگشت به چپ یا راست رد یا به روز کند.

سطح API

از 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 وضعیت جزء را مدیریت می کند. هنگامی که تعامل با مورد انجام شد، callback confirmValueChange فعال می کند. بدنه تماس با اعمال مختلف ممکن انجام می شود. callback یک بولی برمی‌گرداند که به مؤلفه می‌گوید که آیا باید انیمیشن رد کردن را نمایش دهد یا خیر. در این مورد:
    • اگر مورد از ابتدا تا انتها کشیده شود، onToggleDone lambda را فراخوانی می‌کند و todoItem فعلی را ارسال می‌کند. این با به روز رسانی مورد کار مطابقت دارد.
    • اگر مورد از پایان به شروع حرکت داده شود، onRemove lambda را فراخوانی می‌کند و todoItem فعلی را ارسال می‌کند. این با حذف مورد کار مطابقت دارد.
    • it != StartToEnd : این خط اگر جهت کش رفتن StartToEnd نباشد true برمی گرداند و در غیر این صورت 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 قسمت هایی از UI را که به آن وابسته است دوباره ترکیب می کند.
    • در داخل 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 مراجعه کنید.

منابع اضافی