Android RecyclerView добавление и удаление элементов

144

У меня есть RecyclerView с текстовым полем TextView и кросс-кнопка ImageView. У меня есть кнопка за пределами окна просмотра, которая делает видимую / исчезнувшую перекрестную кнопку ImageView.

Я ищу, чтобы удалить элемент из recylerview, когда эти элементы крест кнопка ImageView нажата.

Мой адаптер:

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> implements View.OnClickListener, View.OnLongClickListener {

    private ArrayList<String> mDataset;
    private static Context sContext;

    public MyAdapter(Context context, ArrayList<String> myDataset) {
        mDataset = myDataset;
        sContext = context;
    }

    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_text_view, parent, false);

        ViewHolder holder = new ViewHolder(v);
        holder.mNameTextView.setOnClickListener(MyAdapter.this);
        holder.mNameTextView.setOnLongClickListener(MyAdapter.this);

        holder.mNameTextView.setTag(holder);

        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {

        holder.mNameTextView.setText(mDataset.get(position));

    }

    @Override
    public int getItemCount() {
        return mDataset.size();
    }


    @Override
    public void onClick(View view) {
        ViewHolder holder = (ViewHolder) view.getTag();
        if (view.getId() == holder.mNameTextView.getId()) {
            Toast.makeText(sContext, holder.mNameTextView.getText(), Toast.LENGTH_SHORT).show();
        }
    }


    @Override
    public boolean onLongClick(View view) {
        ViewHolder holder = (ViewHolder) view.getTag();
        if (view.getId() == holder.mNameTextView.getId()) {
            mDataset.remove(holder.getPosition());

            notifyDataSetChanged();

            Toast.makeText(sContext, "Item " + holder.mNameTextView.getText() + " has been removed from list",
                    Toast.LENGTH_SHORT).show();
        }
        return false;
    }

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public TextView mNumberRowTextView;
        public TextView mNameTextView;


        public ViewHolder(View v) {
            super(v);

            mNameTextView = (TextView) v.findViewById(R.id.nameTextView);
        }
    }
}

Мой макет:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_vertical"
    android:id="@+id/layout">

    <TextView
        android:id="@+id/nameTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="18sp"
        android:padding="5dp"
        android:background="@drawable/greyline"/>

    <ImageView
        android:id="@+id/crossButton"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:visibility="gone"
        android:layout_marginLeft="50dp"
        android:src="@drawable/cross" />
</LinearLayout>

Как я могу получить что-то вроде onClick, работающее для моего CrossButton ImageView? Есть ли способ лучше? Может быть, изменив весь элемент на клик, чтобы удалить элемент? В окне повторного просмотра отображается список мест, которые необходимо отредактировать. Любые технические советы или комментарии / предложения по наилучшей реализации будут высоко оценены.

Twentyonehundred
источник
Взгляните LINK 1 LINK 2 это может помочь U
SaravanaRaja
stackoverflow.com/questions/38222410/…
Адитья Вьяс-Лахан
1
Вы можете увидеть этот пример в коде Github Happy !!!!
Cabezas

Ответы:

267

Я сделал что-то подобное. В вашем MyAdapter:

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
    public CardView mCardView;
    public TextView mTextViewTitle;
    public TextView mTextViewContent;
    public ImageView mImageViewContentPic;

    public ImageView imgViewRemoveIcon;
    public ViewHolder(View v) {
        super(v);
        mCardView = (CardView) v.findViewById(R.id.card_view);
        mTextViewTitle = (TextView) v.findViewById(R.id.item_title);
        mTextViewContent = (TextView) v.findViewById(R.id.item_content);
        mImageViewContentPic = (ImageView) v.findViewById(R.id.item_content_pic);
        //......
        imgViewRemoveIcon = (ImageView) v.findViewById(R.id.remove_icon);

        mTextViewContent.setOnClickListener(this);
        imgViewRemoveIcon.setOnClickListener(this);
        v.setOnClickListener(this);
        mTextViewContent.setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View view) {
                if (mItemClickListener != null) {
                    mItemClickListener.onItemClick(view, getPosition());
                }
                return false;
            }
        });
    }


    @Override
    public void onClick(View v) {
        //Log.d("View: ", v.toString());
        //Toast.makeText(v.getContext(), mTextViewTitle.getText() + " position = " + getPosition(), Toast.LENGTH_SHORT).show();
        if(v.equals(imgViewRemoveIcon)){
            removeAt(getPosition());
        }else if (mItemClickListener != null) {
            mItemClickListener.onItemClick(v, getPosition());
        }
    }
}

public void setOnItemClickListener(final OnItemClickListener mItemClickListener) {
    this.mItemClickListener = mItemClickListener;
}
public void removeAt(int position) {
    mDataset.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, mDataSet.size());
}

Надеюсь это поможет.

Редактировать:

getPosition()устарела, используйте getAdapterPosition()вместо этого.

paradite
источник
50
Вы также захотите добавить notifyItemRangeChanged(getPosition, mDataSet.size());ответ Парадита после удаления предмета, чтобы все представления под удаленным предметом были соответственно скорректированы.
Крис Энджелл
3
У меня та же проблема, но getPosition()теперь она устарела
чип
4
Спасибо, видимо, просто звонить notifyItemRemoved(position)было недостаточно.
Ektos974
3
@chip Используйте getAdapterPosition ()
Тайлер Пфафф
2
этот код не устраняет проблему, когда вы быстро удаляете элементы сверху и снизу.
Вито Валов
60

Прежде всего, элемент должен быть удален из списка!

  mDataSet.remove(getAdapterPosition());

затем:

  notifyItemRemoved(getAdapterPosition());
  notifyItemRangeChanged(getAdapterPosition(),mDataSet.size());
Zahra.HY
источник
2
спасибо, что слишком много, предположим, что я должен добавить больше значений означает, что я могу сделать для этого.
МоханРадж С
это связано с размером списка в вашем адаптере. @
Hammad
Я сделал то же самое, но высота реселлера не динамически изменилась. mNotes.remove (положение); notifyItemRemoved (положение); notifyItemRangeChanged (position, getItemCount ()); @ Zahra.HY я что-то пропустил.
Хитеш Дхамшания
22

если этот предмет не удален, используйте этот магический метод :)

private void deleteItem(int position) {
        mDataSet.remove(position);
        notifyItemRemoved(position);
        notifyItemRangeChanged(position, mDataSet.size());
        holder.itemView.setVisibility(View.GONE);
}

Котлин версия

private fun deleteItem(position: Int) {
    mDataSet.removeAt(position)
    notifyItemRemoved(position)
    notifyItemRangeChanged(position, mDataSet.size)
    holder.itemView.visibility = View.GONE
}
Мостафа Антер
источник
@Mostafa: Предположим, у меня есть 10 элементов, и я удаляю последний, в этом сценарии holder.itemView.setVisibility (View.GONE), Скрыть все данные,
Хитеш Дхамшания,
@HiteshDhamshaniya нет, это просто скрыть и уйти последний вид
Мостафа Антер
Не нужно скрывать вид. dataSet.remove (position) работает нормально, но вам нужно вызвать notifyDatasetChamged () или notifyItemRemoved (dataset.size () - 1)
Кару Бенсон Кару
спасибо за holder.itemView.setVisibility (View.GONE);
К.Хайоев
10

проблема

RecyclerViewпо умолчанию не знает изменений вашего набора данных. Это означает, что всякий раз, когда вы делаете удаление / добавление в свой список данных, эти изменения не будут отражены непосредственно в вашем RecyclerView.

Решение

RecyclerView предлагает вам несколько способов поделиться своим состоянием набора данных. Это происходит потому, что изменение вашего набора данных не подразумевает изменения представления, и наоборот. Стандартные API-интерфейсы Android позволяют связать процесс удаления данных (для целей вопроса) с процессом удаления представлений.

notifyItemChanged(index: Int)
notifyItemInserted(index: Int)
notifyItemRemoved(index: Int)
notifyItemRangeChanged(startPosition: Int, itemCount: Int)
notifyItemRangeInserted(startPosition: Int, itemCount: Int)
notifyItemRangeRemoved(startPosition: Int, itemCount: Int)

Лучший подход

Если вы не укажете правильно, что происходит при добавлении / удалении элементов (речь идет о представлениях), дети RecyclerView анимируются без ответа из-за отсутствия информации о том, как перемещать различные представления по списку.

Вместо этого следующий код будет точно воспроизводить анимацию только на IndexOutOfBoundExceptionудаленном дочернем элементе (и, как примечание, для меня он исправил любые s, помеченные стековой трассировкой как «несогласованность данных»)

void remove(position: Int) {
    dataset.removeAt(position)
    notifyItemChanged(position)
    notifyItemRangeRemoved(position, 1)
}

Если заглянуть внутрь, RecyclerViewмы можем найти документацию, объясняющую, что вторым параметром, который мы должны передать, является количество элементов, которые удаляются из набора данных, а не общее количество элементов (как неправильно указано в некоторых других источниках информации).

    /**
     * Notify any registered observers that the <code>itemCount</code> items previously
     * located at <code>positionStart</code> have been removed from the data set. The items
     * previously located at and after <code>positionStart + itemCount</code> may now be found
     * at <code>oldPosition - itemCount</code>.
     *
     * <p>This is a structural change event. Representations of other existing items in the data
     * set are still considered up to date and will not be rebound, though their positions
     * may be altered.</p>
     *
     * @param positionStart Previous position of the first item that was removed
     * @param itemCount Number of items removed from the data set
     */
    public final void notifyItemRangeRemoved(int positionStart, int itemCount) {
        mObservable.notifyItemRangeRemoved(positionStart, itemCount);
    }
Андреа Чоккарелли
источник
7

Возможно двойной ответ, но довольно полезный для меня. Вы можете реализовать метод, приведенный ниже, RecyclerView.Adapter<RecyclerView.ViewHolder> и можете использовать этот метод в соответствии с вашими требованиями, я надеюсь, что он будет работать для вас

public void removeItem(@NonNull Object object) {
        mDataSetList.remove(object);
        notifyDataSetChanged();
    }
Хасан Джамиль
источник
5

Вот несколько наглядных дополнительных примеров. Смотрите мой полный ответ для примеров добавления и удаления диапазона.

Добавить один элемент

Добавить "Свинья" в указателе 2.

Вставить один элемент

String item = "Pig";
int insertIndex = 2;
data.add(insertIndex, item);
adapter.notifyItemInserted(insertIndex);

Удалить один элемент

Удалить «Свинью» из списка.

Удалить один элемент

int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);
Suragch
источник
4

Я перепробовал все вышеперечисленные ответы, но вставка или удаление элементов в режиме повторного просмотра вызывает проблемы с положением в наборе данных. Закончилось использование delete(getAdapterPosition());внутри viewHolder, который отлично работал при поиске позиции предметов.

Arun
источник
1
Просто установите слушатель и используйте метод getAdapterPosition () внутри держателя вида. Он вернет позицию элемента.
Арун
4

Проблема, с которой я столкнулся, заключалась в том, что я удалял из списка элемент, который больше не был связан с адаптером, чтобы убедиться, что вы модифицируете правильный адаптер, для которого вы можете реализовать такой метод в своем адаптере:

public void removeItemAtPosition(int position) {
    items.remove(position);
}

И назовите это в своем фрагменте или деятельности так:

adapter.removeItemAtPosition(position);
Горацио
источник
2
  public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private List<cardview_widgets> list;

public MyAdapter(Context context, List<cardview_widgets> list) {
    this.context = context;
    this.list = list;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    View view = LayoutInflater.from(this.context).inflate(R.layout.fragment1_one_item,
            viewGroup, false);
    return new MyViewHolder(view);
}

public static class MyViewHolder extends RecyclerView.ViewHolder {
    TextView txt_value;
    TextView txt_category;
    ImageView img_inorex;
    ImageView img_category;
    TextView txt_date;

    public MyViewHolder(@NonNull View itemView) {
        super(itemView);
        txt_value = itemView.findViewById(R.id.id_values);
        txt_category = itemView.findViewById(R.id.id_category);
        img_inorex = itemView.findViewById(R.id.id_inorex);
        img_category = itemView.findViewById(R.id.id_imgcategory);
        txt_date = itemView.findViewById(R.id.id_date);
    }
}

@NonNull
@Override
public void onBindViewHolder(@NonNull final MyViewHolder myViewHolder, int i) {

    myViewHolder.txt_value.setText(String.valueOf(list.get(i).getValuee()));
    myViewHolder.txt_category.setText(list.get(i).getCategory());
    myViewHolder.img_inorex.setBackgroundColor(list.get(i).getImg_inorex());
    myViewHolder.img_category.setImageResource(list.get(i).getImg_category());
    myViewHolder.txt_date.setText(list.get(i).getDate());
    myViewHolder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            list.remove(myViewHolder.getAdapterPosition());
            notifyDataSetChanged();
            return false;
        }
    });
}

@Override
public int getItemCount() {
    return list.size();
}}      

Я надеюсь, что это поможет вам.

Мохамед Бен Ромдхан
источник
1

если вы хотите удалить элемент, вы должны сделать это: сначала удалить элемент:

phones.remove(position);

на следующем шаге вы должны уведомить адаптер утилизатора о том, что вы удаляете элемент с помощью этого кода:

notifyItemRemoved(position);
notifyItemRangeChanged(position, phones.size());

но если вы измените элемент, сделайте так: сначала измените параметр вашего объекта следующим образом:

Service s = services.get(position);
s.done = "Cancel service";
services.set(position,s);

или новый это так:

Service s = new Service();
services.set(position,s);

затем сообщите адаптеру утилизатора, что вы модифицируете элемент с помощью этого кода:

notifyItemChanged(position);
notifyItemRangeChanged(position, services.size());

надежда помогает тебе

Сказал Мухаммед Амин Эмрани
источник
1
  String str = arrayList.get(position);
  arrayList.remove(str);
  MyAdapter.this.notifyDataSetChanged();
Тарун Умат
источник
Это действительно плохая идея, так как вы будете вынуждены полностью перерисовывать предметы с вероятным заметным лагом, если у вас много предметов. Что нужно сделать, это позвонить notifyItemRemoved(position).
Минас Мина
Я правильно понимаю, мы также используем notifyItemRemoved (position) вместо notifyDataSetChanged ()
Тарун Умат,
1

К методу onBindViewHolderнаписать этот код

holder.remove.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Cursor del=dbAdapter.ExecuteQ("delete from TblItem where Id="+values.get(position).getId());
                values.remove(position);
                notifyDataSetChanged();
            }
        });
Диако Хасани
источник
1

Incase Любой, кто хочет реализовать нечто подобное в классе Main вместо класса Adapter, вы можете использовать:

public void removeAt(int position) {
    peopleListUser.remove(position);

    friendsListRecycler.getAdapter().notifyItemRemoved(position);
    friendsListRecycler.getAdapter().notifyItemRangeChanged(position, peopleListUser.size());
}

где friendsListRecycler - это имя адаптера

Лесная баба
источник
0
 //////// set the position
 holder.cancel.setTag(position);


///// click to remove an item from recycler view and an array list
holder.cancel.setOnClickListener(new View.OnClickListener() {
          @Override
          public void onClick(View view) {

            int positionToRemove = (int)view.getTag(); //get the position of the view to delete stored in the tag
            mDataset.remove(positionToRemove);
            notifyDataSetChanged();
                }
            });
Рошан Посакья
источник
0

сделать интерфейс в пользовательский класс адаптера и обработать событие click в представлении переработчика.

 onItemClickListner onItemClickListner;

public void setOnItemClickListner(CommentsAdapter.onItemClickListner onItemClickListner) {
    this.onItemClickListner = onItemClickListner;
}

public interface onItemClickListner {
    void onClick(Contact contact);//pass your object types.
}
    @Override
public void onBindViewHolder(ItemViewHolder holder, int position) {
    // below code handle click event on recycler view item.
    holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            onItemClickListner.onClick(mContectList.get(position));
        }
    });
}

после определения адаптера и привязки к представлению переработчика, называемому ниже кода.

        adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
        @Override
        public void onClick(Contact contact) {
            contectList.remove(contectList.get(contectList.indexOf(contact)));
            adapter.notifyDataSetChanged();
        }
    });
}
Команда Android
источник
0

Если вам интересно, как я, где мы можем получить положение адаптера в методе getadapterposition (); его в viewholder object.so вы должны поместить свой код следующим образом

mdataset.remove(holder.getadapterposition());
Радж Кавадия
источник
0

В деятельности:

mAdapter.updateAt(pos, text, completed);
mAdapter.removeAt(pos);

В вашем адаптере:

void removeAt(int position) {
    list.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, list.size());
}

void updateAt(int position, String text, Boolean completed) {
    TodoEntity todoEntity = list.get(position);
    todoEntity.setText(text);
    todoEntity.setCompleted(completed);
    notifyItemChanged(position);
}
Хай Нгуен
источник