Android RecyclerVer adición y eliminación de elementos


144

Tengo un RecyclerView con un cuadro de texto TextView y un botón de visualización ImageView. Tengo un botón fuera de la vista del reciclador que hace que el botón cruzado ImageView sea visible / desaparecido.

Estoy buscando eliminar un elemento de la vista del reciclador, cuando ese elemento cruza el botón ImageView.

Mi adaptador:

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

Mi diseño es:

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

¿Cómo puedo obtener algo como un onClick que funcione para mi CrossButton ImageView? ¿Hay una mejor manera? ¿Tal vez cambiar todo el elemento al hacer clic en quitar el elemento? La vista de reciclaje muestra una lista de ubicaciones que deben editarse. Cualquier consejo técnico o comentario / sugerencia sobre la mejor implementación sería muy apreciado.


Eche un vistazo LINK 1 LINK 2 puede ayudarlo U
SaravanaRaja


1
¡Puedes ver este ejemplo en el código de Github Happy!
Cabezas

Respuestas:


267

He hecho algo similar En su 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());
}

Espero que esto ayude.

Editar:

getPosition()ahora está en desuso, use getAdapterPosition()en su lugar.


50
También querrá agregar notifyItemRangeChanged(getPosition, mDataSet.size());a la respuesta de paradite después de eliminar el elemento para que todas las vistas debajo del elemento eliminado se ajusten en consecuencia.
Chris Angell

3
Tengo el mismo problema pero getPosition()ahora está en desuso
chip

44
Gracias, aparentemente solo llamar notifyItemRemoved(position)no fue suficiente.
Ektos974

3
@chip Use getAdapterPosition ()
Tyler Pfaff

2
Este código no soluciona un problema cuando elimina rápidamente elementos de la parte superior e inferior.
Vito Valov

60

En primer lugar, el elemento debe eliminarse de la lista.

  mDataSet.remove(getAdapterPosition());

luego:

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

2
gracias, mucho, supongo que tengo que agregar más valores significa qué puedo hacer para eso.
MohanRaj S

está relacionado con el tamaño de la lista en su adaptador. @ Hammad Nasir
Zahra.HY

Hice lo mismo, pero la altura de la vista del reciclador no cambió dinámicamente. mNotes.remove (posición); notifyItemRemoved (posición); notifyItemRangeChanged (position, getItemCount ()); @ Zahra.HY me perdí algo.
Hitesh Dhamshaniya

22

si todavía no se eliminó el elemento, use este método mágico :)

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

Versión Kotlin

private fun deleteItem(position: Int) {
    mDataSet.removeAt(position)
    notifyItemRemoved(position)
    notifyItemRangeChanged(position, mDataSet.size)
    holder.itemView.visibility = View.GONE
}

@Mostafa: Supongamos que tengo 10 artículos y elimino el último, en ese escenario holder.itemView.setVisibility (View.GONE), Ocultar todos los datos,
Hitesh Dhamshaniya

@HiteshDhamshaniya no, solo se escondió y se fue la última vista
Mostafa Anter

No es necesario ocultar la vista. dataSet.remove (posición) funciona bien, pero necesita llamar a notifyDatasetChamged () o notifyItemRemoved (dataset.size () - 1)
Karue Benson Karue

gracias por holder.itemView.setVisibility (View.GONE);
K.Hayoev

10

Problema

RecyclerViewes, por defecto, desconoce los cambios en su conjunto de datos. Esto significa que siempre que realice una eliminación / adición en su lista de datos, esos cambios no se reflejarán directamente en su RecyclerView.

Solución

RecyclerView le ofrece algunos métodos para compartir su estado de conjunto de datos. Esto sucede porque una modificación en su conjunto de datos no implica un cambio de vista, y viceversa. Las API de Android estándar le permiten vincular el proceso de eliminación de datos (a los efectos de la pregunta) con el proceso de eliminación de vistas.

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

Mejor enfoque

Si no especifica correctamente lo que sucede al agregar / eliminar elementos (hablando de vistas), los niños RecyclerView se animan sin responder debido a la falta de información sobre cómo mover las diferentes vistas alrededor de la lista.

En cambio, el siguiente código reproducirá con precisión la animación, solo en el elemento secundario que se está eliminando (y como nota al margen, para mí arregló cualquier IndexOutOfBoundExceptions, marcado por el seguimiento de la pila como "inconsistencia de datos")

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

Debajo del capó, si analizamos RecyclerViewpodemos encontrar documentación que explica que el segundo parámetro que tenemos que pasar es el número de elementos que se eliminan del conjunto de datos, no el número total de elementos (como se indicó erróneamente en algunas otras fuentes de información).

    /**
     * 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

Posiblemente una respuesta duplicada pero bastante útil para mí. Puede implementar el método que se proporciona a continuación RecyclerView.Adapter<RecyclerView.ViewHolder> y puede usar este método según sus requisitos, espero que funcione para usted

public void removeItem(@NonNull Object object) {
        mDataSetList.remove(object);
        notifyDataSetChanged();
    }

5

Aquí hay algunos ejemplos visuales suplementarios. Vea mi respuesta más completa para ver ejemplos de agregar y quitar un rango.

Añadir elemento individual

Agregue "Pig" en el índice 2.

Insertar elemento individual

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

Eliminar un solo artículo

Eliminar "Pig" de la lista.

Eliminar un solo artículo

int removeIndex = 2;
data.remove(removeIndex);
adapter.notifyItemRemoved(removeIndex);

4

Intenté todas las respuestas anteriores, pero insertar o eliminar elementos para la vista de reciclaje causa problemas con la posición en el conjunto de datos. Terminé usando delete(getAdapterPosition());dentro de viewHolder, que funcionó muy bien para encontrar la posición de los elementos.


1
Simplemente configure un oyente y use el método getAdapterPosition () dentro del soporte de la vista. Devolverá la posición del artículo.
Arun

4

El problema que tuve fue que estaba eliminando un elemento de la lista que ya no estaba asociado con el adaptador para asegurarme de que está modificando el adaptador correcto, puede implementar un método como este en su adaptador:

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

Y llámalo en tu fragmento o actividad así:

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

Espero que esto te ayude.


1

si desea eliminar el elemento, debe hacer esto: primero elimine el elemento:

phones.remove(position);

en el siguiente paso, debe notificar a su adaptador de reciclador que elimina un artículo mediante este código:

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

pero si cambia un elemento, haga esto: primero cambie un parámetro de su objeto como este:

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

o nuevo así:

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

luego notifique a su adaptador de reciclador que modifica un artículo con este código:

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

La esperanza te ayuda.


1
  String str = arrayList.get(position);
  arrayList.remove(str);
  MyAdapter.this.notifyDataSetChanged();

Esta es una muy mala idea, ya que forzará un nuevo sorteo completo de los elementos, con un retraso notable si tiene muchos elementos. Lo correcto es llamar notifyItemRemoved(position).
Minas Mina

ya su correcto también usamos notifyItemRemoved (posición) instaladas de notifyDataSetChanged ()
Tarun Umath

1

Al método onBindViewHolderescriba este código

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

En caso de que alguien quiera implementar algo como esto en la clase Main en lugar de la clase Adapter, puede usar:

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

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

donde friendsListRecycler es el nombre del adaptador


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

convertir la interfaz en una clase de adaptador personalizada y gestionar el evento de clic en la vista de reciclador.

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

después de definir el adaptador y vincularlo a la vista del reciclador llamado código a continuación.

        adapter.setOnItemClickListner(new CommentsAdapter.onItemClickListner() {
        @Override
        public void onClick(Contact contact) {
            contectList.remove(contectList.get(contectList.indexOf(contact)));
            adapter.notifyDataSetChanged();
        }
    });
}

0

En caso de que se pregunte como yo, ¿dónde podemos obtener la posición del adaptador en el método getadapterposition (); está en el objeto viewholder.así que tienes que poner tu código así

mdataset.remove(holder.getadapterposition());

0

En la actividad:

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

En el su adaptador:

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);
}
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.