используя 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);
}

Ответ 1

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

Ответ 2

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

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();
}

Ответ 3

Пытался

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

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

Ответ 4

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

как это:

public void removeData(int position) {
    yourdatalist.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position,getItemCount());
}

Ответ 5

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

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

holder.removeButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //Position for remove
                int modPosition= holder.getLayoutPosition();
                //remove item from dataset
                numbers.remove(modPosition);
                //remove item from recycler view
                notifyItemRemoved(modPosition);
            }
        });

Ответ 6

**my solution looks like this**

this way is unnecessary to use the heavy method:
 //notifyItemRangeChanged(xx,xx)

/**
 * 
 * recyclerView的item中的某一个view,获取其最外层的viewParent,也就是item对应的layout在adapter中的position
 *
 * @param recyclerView
 * @param view:can be the deep one inside the item,or the item itself .
 * @return
 */
public static int getParentAdapterPosition(RecyclerView recyclerView, View view, int parentId) {
    if (view.getId() == parentId)
        return recyclerView.getChildAdapterPosition(view);
    View viewGroup = (View) view.getParent();
    if (viewGroup != null && viewGroup.getId() == parentId) {
        return recyclerView.getChildAdapterPosition(viewGroup);
    }
    //recursion
    return getParentAdapterPosition(recyclerView, viewGroup, parentId);
}




//wherever you set the clickListener .
holder.setOnClickListener(R.id.rLayout_device_item, deviceItemClickListener);
holder.setOnLongClickListener(R.id.rLayout_device_item, deviceItemLongClickListener);


@Override
public boolean onLongClick(View v) {
    final int position = ViewUtils.getParentAdapterPosition(rVDevicesList, v, R.id.rLayout_device_item);
    return true;
}

Ответ 7

В моем случае я использую 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;

Ответ 8

  mList.remove(position);
  feedListAdapter.notifyItemRemoved(position);
  feedListAdapter.notifyItemChanged(position);

Ответ 9

Вы должны добавить remove listener в класс 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);
    }