Android RecyclerVer adição e remoção de itens

144

Eu tenho um RecyclerView com uma caixa de texto TextView e um botão cruzado ImageView. Eu tenho um botão fora da visão de reciclagem que torna o botão cruzado ImageView visível / desapareceu.

Estou procurando remover um item da recylerview, quando esse item cruza o botão ImageView é pressionado.

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

Meu layout é:

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

Como posso obter algo como um onClick trabalhando no meu crossButton ImageView? Existe uma maneira melhor? Talvez mudar o item inteiro ao clicar em remover o item? A visão de reciclagem mostra uma lista de locais que precisam ser editados. Qualquer conselho técnico ou comentários / sugestões sobre a melhor implementação seriam muito apreciados.

Twentyonehundred
fonte
Dê uma olhada LINK 1 LINK 2 pode ajudar U
SaravanaRaja 3/14
stackoverflow.com/questions/38222410/…
Aditya Vyas-Lakhan 07/07
1
Você pode ver este exemplo no código do Github Happy !!!!
Cabezas

Respostas:

267

Eu fiz algo semelhante. Na tuaMyAdapter :

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 isto ajude.

Editar:

getPosition()está obsoleto agora, use em seu getAdapterPosition()lugar.

paradítico
fonte
50
você também desejará adicionar notifyItemRangeChanged(getPosition, mDataSet.size());à resposta do paradita depois de remover o item, para que todas as visualizações abaixo do item removido se ajustem adequadamente.
precisa
3
Eu tenho o mesmo problema, mas getPosition()agora está obsoleto
chip
4
Obrigado, aparentemente, apenas ligar notifyItemRemoved(position)não foi suficiente.
Ektos974
3
@chip Use getAdapterPosition ()
Tyler Pfaff
2
esse código não corrige um problema quando você remove itens rapidamente de cima e de baixo.
Vito Valov
60

Antes de tudo, o item deve ser removido da lista!

  mDataSet.remove(getAdapterPosition());

então:

  notifyItemRemoved(getAdapterPosition());
  notifyItemRangeChanged(getAdapterPosition(),mDataSet.size());
Zahra.HY
fonte
2
obrigado seu excesso, suponha que eu tenho que adicionar mais valores significa o que posso fazer por isso.
precisa saber é o seguinte
está relacionado ao tamanho da lista no seu adaptador. @ Hammad Nasir
Zahra.HY
Fiz o mesmo, mas a altura da reciclerview não mudou dinamicamente. mNotes.remove (posição); notifyItemRemoved (position); notifyItemRangeChanged (position, getItemCount ()); @ Zahra.HY eu perdi alguma coisa.
Hitesh Dhamshaniya 10/11
22

se o item ainda não for removido, 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);
}

Versão Kotlin

private fun deleteItem(position: Int) {
    mDataSet.removeAt(position)
    notifyItemRemoved(position)
    notifyItemRangeChanged(position, mDataSet.size)
    holder.itemView.visibility = View.GONE
}
Mostafa Anter
fonte
@Mostafa: Suponha que eu tenho 10 artigo e eu remover o último, em que holder.itemView.setVisibility cenário (View.GONE), Esconder todos os dados,
Hitesh Dhamshaniya
@HiteshDhamshaniya não, apenas oculte e se foi a última visualização #
Mostafa Anter
Não há necessidade de ocultar a vista. dataSet.remove (position) funciona bem, mas você precisa chamar notifyDatasetChamged () ou notifyItemRemoved (dataset.size () - 1)
Karue Benson Karue
obrigado por holder.itemView.setVisibility (View.GONE);
K.Hayoev
10

Problema

RecyclerViewé, por padrão, inconsciente das alterações no seu conjunto de dados. Isso significa que sempre que você excluir ou adicionar sua lista de dados, essas alterações não serão refletidas diretamente no seu RecyclerView.

Solução

O RecyclerView oferece alguns métodos para compartilhar seu estado do conjunto de dados. Isso acontece porque uma modificação no seu conjunto de dados não implica uma alteração na exibição e vice-versa. As APIs padrão do Android permitem vincular o processo de remoção de dados (para os fins da pergunta) ao processo de remoção de visualizações.

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

Melhor Abordagem

Se você não especificar corretamente o que acontece com a adição / remoção de itens (falando sobre visualizações), os filhos do RecyclerView são animados sem resposta devido à falta de informações sobre como mover as diferentes visualizações pela lista.

Em vez disso, o código a seguir reproduz com precisão a animação, apenas no filho que está sendo removido (e, como observação adicional, para mim ele corrigiu qualquer IndexOutOfBoundExceptions, marcado pelo stacktrace como "inconsistência de dados")

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

Sob o capô, se examinarmos RecyclerView, podemos encontrar documentação explicando que o segundo parâmetro que precisamos passar é o número de itens que são removidos do conjunto de dados, não o número total de itens (conforme afirmado incorretamente em outras fontes de informação).

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

Possivelmente uma resposta duplicada, mas bastante útil para mim. Você pode implementar o método fornecido abaixo RecyclerView.Adapter<RecyclerView.ViewHolder> e usar esse método de acordo com seus requisitos. Espero que funcione para você

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

Aqui estão alguns exemplos visuais suplementares. Veja minha resposta completa para exemplos de adição e remoção de um intervalo.

Adicionar item único

Adicione "Pig" no índice 2.

Inserir item único

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

Remover item único

Remova "Pig" da lista.

Remover item único

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

Eu tentei todas as respostas acima, mas a inserção ou remoção de itens na reciclagem causa problemas com a posição no dataSet. Acabou usando delete(getAdapterPosition());dentro do viewHolder, que funcionou muito bem para encontrar a posição dos itens.

Uma corrida
fonte
1
Basta definir um ouvinte e usar o método getAdapterPosition () dentro do suporte da visualização. Retornará a posição do item.
Arun
4

O problema que tive foi remover um item da lista que não estava mais associado ao adaptador para garantir que você esteja modificando o adaptador correto. Você pode implementar um método como este no seu adaptador:

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

E chame-o em seu fragmento ou atividade como esta:

adapter.removeItemAtPosition(position);
Horatio
fonte
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 isso lhe ajude.

Mohamed Ben Romdhane
fonte
1

se você deseja remover o item, faça o seguinte: primeiro remova o item:

phones.remove(position);

na próxima etapa, você deve notificar seu adaptador de reciclagem que remove um item por este código:

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

mas se você alterar um item, faça o seguinte: primeiro altere um parâmetro do seu objeto como este:

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

ou novo assim:

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

em seguida, notifique seu adaptador de reciclagem que você modifica um item por este código:

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

a esperança ajuda você.

Sayed Mohammad Amin Emrani
fonte
1
  String str = arrayList.get(position);
  arrayList.remove(str);
  MyAdapter.this.notifyDataSetChanged();
Tarun Umath
fonte
Essa é uma péssima idéia, pois você forçará uma reescrição completa dos itens, com atraso provavelmente perceptível se você tiver muitos itens. A coisa correta a fazer é ligar notifyItemRemoved(position).
Minas Mina
ya seu correto, também usamos notifyItemRemoved (position) instated de notifyDataSetChanged ()
Tarun Umath
1

To Method onBindViewHolderEscreva 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();
            }
        });
Diako Hasani
fonte
1

Caso alguém queira implementar algo parecido com isso na classe Main, em vez da classe Adapter, você pode usar:

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

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

em que friendsListRecycler é o nome do adaptador

Babá da floresta
fonte
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
fonte
0

faça interface na classe do adaptador personalizado e manipule o evento click na visualização do 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));
        }
    });
}

depois de definir o adaptador e vincular à visualização do reciclador chamada abaixo do código ..

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

Caso você esteja se perguntando, como eu fiz, onde podemos obter a posição do adaptador no método getadapterposition (); é no viewholder object.so você tem que colocar seu código assim

mdataset.remove(holder.getadapterposition());
raj kavadia
fonte
0

Na atividade:

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

No seu 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);
}
Huy Nguyen
fonte