RecyclerView не удаляет элементы, хотя список имеет правильный номер элемента

У меня странный случай с recyclerView (с GridLayoutManager). Когда я удаляю элемент и обновляю его с помощью (notifyItemRemoved и notifyItemRangeChanged/notifyDataSetChanged), он не удаляет элемент, а помечает его для удаления (он выделен серым цветом). Если элемент не последний, то он как бы удаляется, но не совсем - он просто под другим элементом и не виден (если добавить к этому элементу дополнительный вид - видно, что он там есть), но если он последний вы можете видеть, что это серым цветом.

Я не первый раз работаю с recyclerView и раньше с ним не сталкивался, но с GridLayoutManager впервые, так что это может быть причиной.

Я отладил код и ясно вижу, что список элементов содержит правильные элементы (4) после удаления 5-го в onBindViewHolder, когда он воссоздает recyclerView после обновления.

Вот функция удаления, когда нажимается отмеченный значок, он получает значок X и появляется диалоговое окно.

private class DeleteMessageDialog extends MessageDialog {
    List<Item> expenses;

    public DeleteMessageDialog(String message, List<Item> expenses) {
        super(DELETE_CATEGORY, WARNING, message, activity);
        this.expenses = expenses;
    }

    @Override
    protected void okPressed() {
        DataHelper.getDataHelper(activity).removeItems(expenses);
        viewBuilder.refreshItems(DELETE_ITEM);
        dismiss();
    }
}

Метод обновления (я жестко запрограммировал 4 - такое же быстрое и грязное решение, как и до того, как у меня было notifyDataSetChanged) и изменил значения в соответствии с комментарием Phuc:

public void refreshItems(Enums.Action action) {
    dataHelper.setListOfCategoryItems();
    List<Category> categoryItems = Utils.getCategoryItems(Utils.NO_PARENT_PREDICATE);
    categoryItemAdapter.updateData(categoryItems);
    MonthlyStatistics statistics = DataHelper.getDataHelper(activity).getMonthlyStatistics(Utils.getCurrentDate(PAY));
    statBuilder.populateStatistics(statistics.getStatistics().get(category));
    if (action == ADD_ITEM)
    {
        activity.moveToMainPage();
        categoryItemAdapter.notifyDataSetChanged();
    }
    if (action == DELETE_ITEM)
    {
        categoryItemAdapter.notifyItemRemoved(4);
        categoryItemAdapter.notifyItemRangeChanged(4, 1);
    }

}

Метод updateData

public void updateData(List<Category> categoryItems) {
    this.categoryItems.clear();
    this.categoryItems.addAll(categoryItems);
    this.parentsAndChildren.clear();
    this.parentsAndChildren.putAll(getChildrenAndParents(categoryItems));
}

введите здесь описание изображения

В режиме отладки onBindViewHolder может видеть, что осталось только 4 правильных элемента введите здесь описание изображения

После того, как все закончилось, вот результат:

введите здесь описание изображения

Если я удалю 3-й элемент (не последний), результат будет выглядеть так:

введите здесь описание изображения

Я облазил весь интернет и не нашел решения, поэтому надеюсь, что публикация здесь поможет. Ценю все ответы. Спасибо.

РЕДАКТИРОВАТЬ: Как предложил Камаль Наян, если снова создать адаптер и поместить его в recyclerView, это сработает, но я почти уверен, что есть лучшее решение, и был бы признателен, если кто-нибудь его найдет.


person Marat    schedule 23.04.2021    source источник
comment
Пробовали снова поставить адаптер?   -  person Kamal Nayan    schedule 23.04.2021
comment
@KamalNayan Я избегал этого решения, но да, установка адаптера снова работает, но я почти уверен, что это неправильный способ сделать это, поскольку у нас есть все методы уведомления, чтобы сделать именно это. Спасибо за предложение.   -  person Marat    schedule 23.04.2021
comment
Если вы хотите поделиться демонстрацией этого, чтобы мы могли внимательно посмотреть, так как это странное поведение   -  person Zain    schedule 24.04.2021
comment
@Zain Это в значительной степени демонстрация со скриншотами. Скриншоты расположены в правильном порядке. После нажатия на какой-либо значок появляется сообщение об удалении. Нажав кнопку УДАЛИТЬ, что результат вы видите..   -  person Marat    schedule 24.04.2021


Ответы (1)


Вы злоупотребили функциональностью notifyItemRemoved и notifyRangeChanged. В документации:

notifyItemRemoved (внутренняя позиция)

with position — это позиция удаленного элемента. В вашем случае вы удаляете 5-й элемент, поэтому вы должны передать 4 вместо 5.

notifyRangeChanged (int positionStart, itemCount)

  • positionStart: позиция первого измененного элемента
  • itemCount: количество элементов, которые были изменены

Я не думаю, что этот метод необходим, если вы уже вызвали notifyItemRemoved(), но вы должны передать 4 для positionStart и 1 для itemCount, потому что вы меняете только 5-й элемент.

EDIT: мне кажется, я вижу еще одну проблему в вашем коде. Всякий раз, когда вы удаляете элемент, вы очищаете старый список, а затем передаете новый список адаптеру. Поскольку предыдущий список, прикрепленный к адаптеру, был удален, а новый присоединен, ваш вызов notifyItemRemoved() не имеет смысла, поскольку в новом списке ничего не изменилось. Это приводит к неожиданному поведению ресайклера, с которым вы сталкиваетесь. Мое предлагаемое решение заключается в том, что вы изменяете список в своем адаптере вместо того, чтобы создавать новые при каждом удалении. Например, объявите метод deleteItem(int position) в вашем адаптере:

public void deleteItem(int position){
     yourList.remove(position);
     notifyItemRemoved(position);
}
person Phúc Nguyễn    schedule 23.04.2021
comment
Вы правы в неправильном использовании этих двух методов, спасибо. Я исправил это, к сожалению, это не решает мою проблему. Решение, предложенное Камалем, действительно работает, но я уверен, что это неправильный способ. - person Marat; 23.04.2021
comment
Да, новый переходник ставить не надо. Пожалуйста, взгляните на мой отредактированный ответ - person Phúc Nguyễn; 23.04.2021
comment
Хотя я думаю, что список - это тот же список (когда я добавляю элементы из списка, который я передаю в список состояний), я сделал то, что вы предложили (явно удалил элемент из списка, не добавляя элементы из другого списка), и все же получил тот же результат. - person Marat; 23.04.2021
comment
Да, извините, возможно, вы правы. Но по крайней мере мой путь отрежет все clear() и addAll(). О проблеме, так странно. Вы изменили GridLayout на другие менеджеры компоновки (например, LinearLayoutManager), проблема все еще существует? - person Phúc Nguyễn; 24.04.2021
comment
да, я изменил его сейчас на Linear. Та же проблема. Мне кажется магией. На данный момент единственное работающее решение — перезагрузить адаптер, чего я стараюсь избегать. - person Marat; 24.04.2021