Додавання та видалення елементів Android RecyclerView


144

У мене є RecyclerView з текстовим полем TextView та перехресною кнопкою ImageView. У мене є кнопка поза рециркулятора, яка робить перехресну кнопку ImageView видимою / знищеною.

Я хочу вилучити елемент з перегляду рецилер, коли ці елементи натискають перехресну кнопку 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? Чи є кращий спосіб? Можливо, змінивши весь елемент onclick на видалити елемент? Рециркулятор показує список місць, які потрібно відредагувати. Будемо дуже вдячні за будь-які технічні поради чи коментарі / пропозиції щодо найкращого впровадження.


Подивіться ЛИНКУ 1 ПОСИЛАННЯ 2, це може допомогти U
SaravanaRaja


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()замість цього.


50
ви також захочете додати відповідь notifyItemRangeChanged(getPosition, mDataSet.size());до парадиту після того, як ви вилучите елемент, так що всі перегляди нижче цього пункту будуть відповідно відрегульовані.
Кріс Енджелл

3
У мене така ж проблема, але getPosition()зараз застаріла
фішка

4
Дякую, мабуть, просто дзвонити notifyItemRemoved(position)було недостатньо.
Ektos974

3
@chip Використовуйте getAdapterPosition ()
Тайлер Пфафф

2
цей код не виправляє проблему, коли ви швидко видаляєте елементи зверху та знизу.
Віто Валов

61

Перш за все, пункт слід вилучити зі списку!

  mDataSet.remove(getAdapterPosition());

тоді:

  notifyItemRemoved(getAdapterPosition());
  notifyItemRangeChanged(getAdapterPosition(),mDataSet.size());

2
дякую вашій великій кількості, припустимо, я повинен додати більше значень означає, що я можу зробити для цього.
MohanRaj S

це пов'язано з розміром списку у вашому адаптері. @
Hammad

Я зробив те ж саме, але висота reciklierview не динамічно змінилася. mNotes.remove (положення); notifyItemRemoved (позиція); notifyItemRangeChanged (позиція, getItemCount ()); @ Zahra.HY я нічого не пропустив.
Hitesh Dhamshaniya

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 елемент, і я видаляю останній, у цьому сценарії власник.itemView.setVisibility (View.GONE), приховати всі дані,
Hitesh Dhamshaniya

@HiteshDhamshaniya ні, це просто приховати і пішов останній погляд
Mostafa Anter

Не потрібно приховувати вигляд. dataSet.remove (позиція) працює нормально, але yoi потрібно зателефонувати notifyDatasetChamged () або notifyItemRemoved (database.size () - 1)
Karue Benson Karue

дякую за hold.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 анімують безвідповідально через брак інформації про те, як переміщувати різні погляди навколо списку.

Натомість наступний код точно відтворюватиме анімацію саме на дочірній частині, яку вилучають (і як бічна примітка, для мене він виправив будь-які IndexOutOfBoundExceptions, позначені стек-трасом як "невідповідність даних")

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

4

Я спробував усі вищевказані відповіді, але вставка або видалення елементів для recilerview викликає проблеми з позицією в DataSet. Закінчився використанням delete(getAdapterPosition());всередині viewHolder, який чудово працював у пошуку позиції елементів.


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 (позиція), встановлену notifyDataSetChanged ()
Tarun Umath

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

У випадку, якщо хто-небудь хоче реалізувати щось подібне в основному класі замість класу 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

перетворити інтерфейс у спеціальний клас адаптера та керувати подіями клацання на перегляді рециркулятора ..

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

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);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.