Всегда ли Compose запоминает старый элемент на основе идентификатора, даже если список был обновлен до более нового?

У меня есть простой пример приложения, которое может

  • Загрузить новый список (из 2 элементов, с идентификаторами 0 и 1 и случайным текстом для каждого)
  • Он может провести пальцем, чтобы закрыть любой элемент.

Если я

  • загрузить новый список в первый раз
  • проведите пальцем, чтобы удалить первый элемент
  • загрузить новый список (с тем же идентификатором, но другим случайным текстом)
  • проведите пальцем, чтобы удалить второй элемент

Он выйдет из строя, как показано на GIF ниже (вы можете получить дизайн кода здесь https://github.com/elye/issue_android_jetpack_compose_swipe_to_dismiss_ Different_data_same_id)

Причина в сбое, потому что при пролистывании для отклонения 2-го элемента (из данных, загруженных во второй раз) найденный элемент по-прежнему является 2-м элементом данных, загруженных в первый раз.

Кажется, что rejectState (как показано ниже) всегда помнит данные, загруженные в первый раз (вместо новых загруженных данных)

                      val dismissState = rememberDismissState(
                    confirmStateChange = {
                        Log.d("Track", "$item\n${myListState.value.toMutableList()}")
                        viewModel.removeItem(item)
                        true
                    }
                )

Следовательно, это приводит к тому, что при удалении отправляется неправильный элемент для удаления, что приводит к сбою и сбою.

Полный код LazyColumn и SwipeToDismiss показан ниже.

             LazyColumn(modifier = Modifier.fillMaxHeight()) {
            items(
                items = myListState.value,
                key = { todoItem -> todoItem.id }
            ) { item ->
                val dismissState = rememberDismissState(
                    confirmStateChange = {
                        viewModel.removeItem(item)
                        true
                    }
                )

                SwipeToDismiss(
                    state = dismissState,
                    background = {
                        dismissState.dismissDirection ?: return@SwipeToDismiss
                        Box(modifier = Modifier.fillMaxSize().background(Color.Red))
                    },
                    dismissContent = {
                        // The row view of each item
                    }
                )
            }
        }

Это

  1. Моя проблема в том, что я упускаю что-либо, чтобы обновить отклонение состояния при загрузке новых данных?
  2. Ошибка Google, из-за которой SwipeToDismiss всегда должен работать со списком уникальных идентификаторов. Даже если список обновляется до нового списка, он не может иметь тот же идентификатор, который совпадает с любым элементом предыдущего списка.
    • то есть если я заменюkey = { todoItem -> todoItem.id }с key = { todoItem -> todoItem.title }, тогда все будет хорошо

1 ответ

rememberDismissState()будет помнитьconfirmStateChangeлямбда, которая является частьюDismissState. В вашем случае может измениться, но лямбда фиксирует только начальнуюitemзначение, приводящее к сбою.

Вы можете использоватьrememberUpdatedStateчтобы решить это:

      val currentItem by rememberUpdatedState(item)
val dismissState = rememberDismissState(
    confirmStateChange = {
        viewModel.removeItem(currentItem)
        true
    }
)
Другие вопросы по тегам