Android RecyclerView ajout et suppression d'éléments

144

J'ai un RecyclerView avec une zone de texte TextView et un bouton croisé ImageView. J'ai un bouton en dehors de la vue de recyclage qui rend le bouton croisé ImageView visible / disparu.

Je cherche à supprimer un élément de la recylerview, lorsque cet élément croise le bouton ImageView est enfoncé.

Mon adaptateur:

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

Ma mise en page est:

<?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>

Comment puis-je faire fonctionner quelque chose comme un onClick pour mon crossButton ImageView? Y a-t-il un meilleur moyen? Peut-être changer l'élément entier en cliquant sur un élément supprimer? La vue de recyclage affiche une liste des emplacements qui doivent être modifiés. Tout conseil technique ou commentaire / suggestion sur la meilleure mise en œuvre serait extrêmement apprécié.

Vingt et un
la source
Jetez un oeil LINK 1 LINK 2 cela peut aider U
SaravanaRaja
1
Vous pouvez voir cet exemple dans Github Happy code !!!!
Cabezas

Réponses:

267

J'ai fait quelque chose de similaire. Dans votre 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());
}

J'espère que cela t'aides.

Éditer:

getPosition()est obsolète maintenant, utilisez à la getAdapterPosition()place.

paradis
la source
50
vous voudrez également ajouter notifyItemRangeChanged(getPosition, mDataSet.size());à la réponse de paradite après avoir supprimé l'élément afin que toutes les vues sous l'élément supprimé s'ajusteront en conséquence.
Chris Angell
3
j'ai le même problème mais getPosition()est maintenant obsolète
puce
4
Merci, apparemment, notifyItemRemoved(position)il ne suffisait pas d' appeler .
Ektos974 du
3
@chip Use getAdapterPosition ()
Tyler Pfaff
2
ce code ne résout pas un problème lorsque vous supprimez rapidement des éléments du haut et du bas.
Vito Valov
60

tout d'abord, l'élément doit être supprimé de la liste!

  mDataSet.remove(getAdapterPosition());

puis:

  notifyItemRemoved(getAdapterPosition());
  notifyItemRangeChanged(getAdapterPosition(),mDataSet.size());
Zahra.HY
la source
2
merci beaucoup, supposons que je doive ajouter plus de valeurs signifie que puis-je faire pour cela.
MohanRaj S
il est lié à la taille de la liste dans votre adaptateur. @ Hammad Nasir
Zahra.HY
J'ai fait de même, mais la hauteur de la vue de recyclage n'a pas changé de manière dynamique. mNotes.remove (position); notifyItemRemoved (position); notifyItemRangeChanged (position, getItemCount ()); @ Zahra.HY ai-je raté quelque chose.
Hitesh Dhamshaniya
22

si l'élément n'est toujours pas supprimé, utilisez cette méthode magique :)

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

Version Kotlin

private fun deleteItem(position: Int) {
    mDataSet.removeAt(position)
    notifyItemRemoved(position)
    notifyItemRangeChanged(position, mDataSet.size)
    holder.itemView.visibility = View.GONE
}
Mostafa Anter
la source
@Mostafa: Supposons que j'ai 10 élément et que je supprime le dernier, dans ce scénario holder.itemView.setVisibility (View.GONE), Cacher toutes les données,
Hitesh Dhamshaniya
@HiteshDhamshaniya non, il suffit de se cacher et de passer la dernière vue
Mostafa Anter
Pas besoin de cacher la vue. dataSet.remove (position) fonctionne correctement, mais vous devez appeler notifyDatasetChamged () ou notifyItemRemoved (dataset.size () - 1)
Karue Benson Karue
merci pour holder.itemView.setVisibility (View.GONE);
K.Hayoev le
10

Problème

RecyclerViewest, par défaut, ignorant des modifications de votre jeu de données. Cela signifie que chaque fois que vous effectuez une suppression / ajout sur votre liste de données, ces modifications ne seront pas répercutées directement sur votre RecyclerView.

Solution

RecyclerView vous propose quelques méthodes pour partager l'état de votre ensemble de données. Cela se produit car une modification de votre ensemble de données n'implique pas de changement de vue, et vice versa. Les API Android standard vous permettent de lier le processus de suppression de données (aux fins de la question) au processus de suppression de vue.

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

Meilleure approche

Si vous ne spécifiez pas correctement ce qui se passe lors de votre ajout / suppression d'éléments (en parlant de vues), les enfants de RecyclerView sont animés sans réponse en raison d'un manque d'informations sur la façon de déplacer les différentes vues dans la liste.

Au lieu de cela, le code suivant lira précisément l'animation, uniquement sur l'enfant qui est en cours de suppression (et en remarque, pour moi, il a corrigé tous les IndexOutOfBoundExceptions, marqués par le stacktrace comme "incohérence des données")

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

Sous le capot, si nous regardons, RecyclerViewnous pouvons trouver de la documentation expliquant que le deuxième paramètre que nous devons passer est le nombre d'éléments supprimés de l'ensemble de données, et non le nombre total d'éléments (comme indiqué à tort dans certaines autres sources d'informations).

    /**
     * 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);
    }
Andrea Cioccarelli
la source
7

Peut-être une réponse en double mais très utile pour moi. Vous pouvez implémenter la méthode indiquée ci-dessous RecyclerView.Adapter<RecyclerView.ViewHolder> et utiliser cette méthode selon vos besoins, j'espère que cela fonctionnera pour vous

public void removeItem(@NonNull Object object) {
        mDataSetList.remove(object);
        notifyDataSetChanged();
    }
Hassan Jamil
la source
5

Voici quelques exemples visuels supplémentaires. Voir ma réponse plus complète pour des exemples d'ajout et de suppression d'une plage.

Ajouter un seul élément

Ajoutez "Pig" à l'index 2.

Insérer un seul élément

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

Supprimer un seul élément

Supprimez "Pig" de la liste.

Supprimer un seul élément

int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);
Suragch
la source
4

J'ai essayé toutes les réponses ci-dessus, mais l'insertion ou la suppression d'éléments dans recyclerview pose un problème avec la position dans le dataSet. delete(getAdapterPosition());J'ai fini par utiliser à l'intérieur de viewHolder qui fonctionnait très bien pour trouver la position des éléments.

Arun
la source
1
Définissez simplement un écouteur et utilisez la méthode getAdapterPosition () dans le support de vue. Il renverra la position de l'élément.
Arun
4

Le problème que j'ai eu était que je supprimais un élément de la liste qui n'était plus associé à l'adaptateur pour m'assurer que vous modifiez l'adaptateur approprié, vous pouvez implémenter une méthode comme celle-ci dans votre adaptateur:

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

Et appelez-le dans votre fragment ou activité comme ceci:

adapter.removeItemAtPosition(position);
Horatio
la source
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();
}}      

j'espère que cela vous aidera.

Mohamed Ben Romdhane
la source
1

si vous souhaitez supprimer un élément, vous devez faire ceci: d'abord supprimer l'élément:

phones.remove(position);

à l'étape suivante, vous devez informer votre adaptateur de recyclage que vous supprimez un élément par ce code:

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

mais si vous modifiez un élément, procédez comme suit: modifiez d'abord un paramètre de votre objet comme ceci:

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

ou nouveau comme ceci:

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

puis informez votre adaptateur de recyclage que vous modifiez un article par ce code:

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

l'espoir vous aide.

Sayed Mohammad Amin Emrani
la source
1
  String str = arrayList.get(position);
  arrayList.remove(str);
  MyAdapter.this.notifyDataSetChanged();
Tarun Umath
la source
C'est une très mauvaise idée, car vous forcerez une redéfinition complète des éléments, avec probablement un retard notable si vous avez beaucoup d'éléments. La bonne chose à faire est d'appeler notifyItemRemoved(position).
Minas Mina
ya votre correct, nous utilisons également notifyItemRemoved (position) instated of notifyDataSetChanged ()
Tarun Umath
1

Méthode pour onBindViewHolderécrire ce code

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();
            }
        });
Diako Hasani
la source
1

Incase Anyone veut implémenter quelque chose comme ça dans la classe Main au lieu de la classe Adapter, vous pouvez utiliser:

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

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

où friendsListRecycler est le nom de l'adaptateur

Baba de la forêt
la source
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();
                }
            });
Roshan Posakya
la source
0

faire de l'interface une classe d'adaptateur personnalisée et gérer l'événement de clic sur la vue du recycleur.

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

après avoir défini l'adaptateur et lié à la vue du recycleur appelé ci-dessous le code.

        adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
        @Override
        public void onClick(Contact contact) {
            contectList.remove(contectList.get(contectList.indexOf(contact)));
            adapter.notifyDataSetChanged();
        }
    });
}
Équipe Android
la source
0

Au cas où vous vous demanderiez comme je l'ai fait, où pouvons-nous obtenir la position de l'adaptateur dans la méthode getadapterposition (); c'est dans l'objet viewholder.vous devez donc mettre votre code comme ceci

mdataset.remove(holder.getadapterposition());
raj kavadia
la source
0

Dans l'activité:

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

Dans votre adaptateur:

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);
}
Huy Nguyen
la source