с помощью notifyItemRemoved или notifyDataSetChanged с RecyclerView в Android

Я создаю список карт для отображения с помощью RecyclerView, где на каждой карте есть кнопка для удаления этой карты из списка.

Когда я использую notifyItemRemoved () для удаления карты в RecyclerView, он удаляет элемент и отлично анимирует, но данные в списке обновляются некорректно.

Если вместо этого я переключаюсь на notifyDataSetChanged (), тогда элементы в списке удаляются и обновляются правильно, но тогда карты не анимируются.

Есть ли у кого-нибудь опыт использования notifyItemRemoved () и знает ли он, почему он ведет себя иначе, чем notifyDataSetChanged?

Вот фрагмент кода, который я использую:

private List<DetectedIssue> issues = new ArrayList<DetectedIssue>();

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // - get element from your dataset at this position
    // - replace the contents of the view with that element
    if(position >0){
        RiskViewHolder riskHolder = (RiskViewHolder)holder;
        final int index = position - 1;
        final DetectedIssue anIssue = issues.get(index);

        riskHolder.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    int index = issues.indexOf(anIssue);
                    issues.remove(anIssue);
                    notifyItemRemoved(index);

                    //notifyDataSetChanged();
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

@Override
public int getItemCount() {
    return (issues.size()+1);
}

person revolutionary    schedule 28.01.2015    source источник
comment
попробуйте notifyItemRemoved (индекс + 1)   -  person pskink    schedule 28.01.2015
comment
Вероятно, потому что вы удаляете разные индексы   -  person Pedro Oliveira    schedule 28.01.2015
comment
Индекс правильный. Как я уже сказал, все работает нормально, если вместо этого я использую notifyDataSetChanged () ...   -  person revolutionary    schedule 28.01.2015
comment
вы пробовали notifyItemRemoved (index + 1)?   -  person pskink    schedule 28.01.2015
comment
Ух ты, моя точная проблема! Спасибо, что избавили меня от необходимости упростить код, чтобы прояснить вопрос.   -  person SMBiggs    schedule 03.08.2016
comment
Имея ту же проблему, index + 1 не работал, и метод Range также не работал.   -  person Jaeger    schedule 10.10.2016
comment
liist.remove (позиция); notifyItemRemoved (позиция); notifyItemRangeChanged (позиция, getItemCount ()); Для удаления каждый раз самого верхнего элемента.   -  person Rohit Bandil    schedule 19.02.2017


Ответы (7)


Используйте notifyItemRangeChanged (position, getItemCount ()); после notifyItemRemoved (position);
Вам не нужно использовать индекс, просто используйте позицию. См. Код ниже.

private List<DetectedIssue> issues = new ArrayList<DetectedIssue>();

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    // - get element from your dataset at this position
    // - replace the contents of the view with that element
    if(position >0){
        RiskViewHolder riskHolder = (RiskViewHolder)holder;

        riskHolder.button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    issues.remove(position);
                    notifyItemRemoved(position);
                    //this line below gives you the animation and also updates the
                    //list items after the deleted item
                    notifyItemRangeChanged(position, getItemCount());

                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

@Override
public int getItemCount() {
    return issues.size();
}
person Akshay Mahajan    schedule 18.12.2015
comment
Из документации: вы должны использовать параметр позиции только при получении связанного элемента данных внутри этого метода и не должны хранить его копию. Если вам понадобится позиция элемента позже (например, в прослушивателе кликов), используйте RecyclerView.ViewHolder.getAdapterPosition (), который будет иметь обновленную позицию адаптера. - person Juan Cruz Soler; 13.12.2016
comment
@Akshay Mahajan Старая ветка извините, но notifyItemRemoved(position); отлично работает в одиночку (с анимацией). Что notifyItemRangeChanged(position, getItemCount()); делает? Я не вижу разницы. Спасибо - person Yohan Dahmani; 03.05.2017
comment
Не должно быть getItemCount () - position, поскольку itemCount означает количество элементов после удаленного элемента? - person Michał Ziobro; 05.08.2017
comment
да, второй параметр - это диапазон изменяемых позиций. использование счетчика элементов означает, что каждый элемент после позиции меняется. - person Travis Castillo; 05.10.2017
comment
@YohanDahmani, notifyItemRemoved (позиция); не будет работать, если вы попробуете последний элемент..IndexOutOfBoundException вы получите. - person Bajrang Hudda; 20.08.2018
comment
Этот ответ бессмысленен. И notifyItemRemoved, и notifyItemRangeChanged делают одно и то же. Почему у этого ответа так много голосов, остается загадкой. - person Manuel; 04.07.2019

Пытался

public void removeItem(int position) {
    this.taskLists.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, getItemCount() - position);
}

и работает как шарм.

person Grender    schedule 16.01.2016
comment
Не могли бы вы объяснить, почему вы используете getItemCount() - position вместо просто getItemCount()? - person hamena314; 16.04.2018
comment
@ hamena314 на самом деле notifyItemRangeChanged (int position, int itemCount), если элементы позиции изменились, а элементы itemCount из позиции изменились, поэтому разумно передавать только элементы после positon вместо передачи списка всех элементов. Или вместо передачи getItemCount () - position мы можем передать getItemCount (). - person Jay; 25.09.2018
comment
Это работает. Большое спасибо - person Rahul Chandrabhan; 29.03.2021

моя ошибка, notifyItemChanged (position) беспомощен, элемент позиции может быть удален, а элемент позиции + 1 в порядке, но элементы начинаются с позиции + 2, вы получите исключение, используйте notifyItemRangeChanged (position, getItemCount ()); после notifyItemRemoved (position);

нравится:

public void removeData(int position) {
    yourdatalist.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position,getItemCount());
}
person TikT    schedule 11.01.2016
comment
пожалуйста, подробно опишите, что изменится в результате этого и как это поможет решить проблему. - person AndroidMechanic - Viral Patel; 11.01.2016
comment
это сработало. положение объекта строки также будет обновлено. - person ralphgabb; 06.12.2020

Как предположил @pskink, в моем случае это должно было быть (index + 1) с notifyItemRemoved(index+1), вероятно, потому, что я резервирую верхний индекс, то есть position=0 для заголовка.

person revolutionary    schedule 28.01.2015

В моем случае я использую Content Provider и Custom RecyclerView Adapter с курсором. В этой строке кода вы уведомляете:

getContext().getContentResolver().notifyChange(uri, null);

Предполагая, что в вашем адаптере recyclerView (кнопка удаления):

Uri currentUri = ContentUris.withAppendedId(DatabaseContract.ToDoEntry.CONTENT_URI_TODO, id);
int rowsDeleted = mContext.getContentResolver().delete(currentUri, null, null);
if (rowsDeleted == 0) {
    Log.d(TAG, "onClick: Delete failed");
} else {
    Log.d(TAG, "onClick: Delete Successful");
}

И в вашем провайдере базы данных:

case TODO_ID:
selection = DatabaseContract.ToDoEntry._ID + "=?";
selectionArgs = new String[] {String.valueOf(ContentUris.parseId(uri))};
rowsDeleted = database.delete(DatabaseContract.ToDoEntry.TODO_TABLE_NAME, selection, selectionArgs);
if (rowsDeleted != 0){
    getContext().getContentResolver().notifyChange(uri, null);
}
return rowsDeleted;
person MohammadL    schedule 16.04.2018

Вы можете использовать getAdapterPosition() из RecyclerView.ViewHolder

getLayoutPosition() обеспечивает точное положение элемента в макете, а код

holder.removeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Position for remove
                int modPosition= holder.getAdapterPosition();
                //remove item from dataset
                numbers.remove(modPosition);
                //remove item from recycler view
                if(numbers.isEmpty())
                  notifyDataSetChanged () 
                else
                  notifyItemRemoved(modPosition);
                
            }
        });
person sree_sg    schedule 17.05.2016
comment
Вы должны использовать getAdapterPosition в качестве документации заявляет, что вы рискуете создать несоответствия. - person marcos E.; 02.02.2017
comment
получение ошибки при удалении последнего элемента списка массивов с помощью этого notifyItemRemoved (last pos) любое решение для такого же? - person Arbaz.in; 08.02.2021
comment
ты можешь это проверить? if (numbers.isEmpty ()) notifyDataSetChanged () else notifyItemRemoved (modPosition); - person sree_sg; 09.02.2021

Вы должны добавить слушателя удаления в класс ViewHolder

 button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                   onCancel(getAdapterPosition());

            }
        });

  private void onCancel(int position) {
        if (position >= issues.size())
            return;
        issues.remove(position);
        notifyItemRemoved(position);
    }
person Kishan Vaghela    schedule 21.09.2015
comment
получение ошибки при установке notifyItemRemoved (последний список массивов pos) - person Arbaz.in; 08.02.2021