Como atualizar / atualizar um item específico no RecyclerView

112

Estou tentando atualizar um item específico em RecyclerView.

História: Sempre que o usuário clica no item, ele é exibido AlertDialog. O usuário pode digitar algum texto clicando no botão ok. Quero mostrar este texto neste item e mostrar invisível ImageView- declarado em XML e adaptador ViewHolder-

Usei esta função no AlertDialogBotão Positivo para atualizar o item:

private void updateListItem(int position) {
  View view = layoutManager.findViewByPosition(position);
  ImageView medicineSelected = (ImageView) view.findViewById(R.id.medicine_selected);
  medicineSelected.setVisibility(View.VISIBLE);
  TextView orderQuantity = (TextView) view.findViewById(R.id.order_quantity);
  orderQuantity.setVisibility(View.VISIBLE);
  orderQuantity.setText(quantity + " packet added!");

  medicinesArrayAdapter.notifyItemChanged(position);
}

Mas este código não apenas altera o itemView na posição passada, mas também altera alguns dos outros itemView (s)!

Como devo alterar um itemView específico corretamente clicando nele?

Elgendy
fonte

Respostas:

98

Você pode usar o notifyItemChanged(int position)método da classe RecyclerView.Adapter. Da documentação:

Notifique qualquer observador registrado que o item na posição mudou. Equivalente a chamar notificarItemChanged (posição, nulo) ;.

Este é um evento de mudança de item, não um evento de mudança estrutural. Isso indica que qualquer reflexo dos dados na posição está desatualizado e deve ser atualizado. O item na posição mantém a mesma identidade.

Como você já tem o cargo, deve funcionar para você.

Edson Menegatti
fonte
9
Já usei, mas se eu quiser atualizar a posição 0, ele está atualizando a posição 0, 9, 19, ...
Elgendy
Desculpe, não entendi seu comentário. Qual posição está atualizando? 0, 9 ou o intervalo entre 0 e 9?
Edson Menegatti
Não, não é intervalo, toda vez que estou atualizando um item, ele também atualiza itens indesejados
Elgendy,
@Learner Quando você atualiza o ViewHolder na posição 0, ele permanece atualizado. O RecyclerView reciclará aquele ViewHolder e o usará para a posição 9, então você verá essas mudanças lá. Eu recomendo definir este texto em onBindViewHolder () - dessa forma, sempre que um ViewHolder for reciclado, você pode verificar sua posição e definir o texto ou tornar a imagem visível ou qualquer outra coisa corretamente.
Cliabhach
@Cliabhach ainda tem o mesmo problema ao fazer mudanças emonBindViewHolder()
Choletski
39

Atualizar item único

  1. Atualize o item de dados
  2. Notifique o adaptador sobre a mudança com notifyItemChanged(updateIndex)

Exemplo

Altere o item "Ovelha" para que diga "Eu gosto de ovelhas".

Atualizar item único

String newValue = "I like sheep.";
int updateIndex = 3;
data.set(updateIndex, newValue);
adapter.notifyItemChanged(updateIndex);

Minha resposta completa com mais exemplos está aqui .

Suragch
fonte
como fazer isso se eu quiser que os dados atualizados mudem para o topo
Choy
@Choy, depois de atualizar os dados, você pode movê-los para o índice 0. Consulte a seção Mover item único deste item
Suragch
11

Adicione o texto alterado à sua lista de dados do modelo

mdata.get(position).setSuborderStatusId("5");
mdata.get(position).setSuborderStatus("cancelled");
notifyItemChanged(position);
Shamir Kp
fonte
10

Acho que tenho uma ideia de como lidar com isso. Atualizar é o mesmo que excluir e recolocar na posição exata. Então, primeiro removo o item dessa posição usando o código abaixo:

public void removeItem(int position){
    mData.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, mData.size());
}

e então eu adicionaria o item naquela posição específica, conforme mostrado abaixo:

public void addItem(int position, Landscape landscape){
    mData.add(position, landscape);
    notifyItemInserted(position);
    notifyItemRangeChanged(position, mData.size());
}

Estou tentando implementar isso agora. Gostaria de lhe dar um feedback quando terminar!

EGBENCHONG II J. Ayuk
fonte
2
Você está realizando uma etapa extra. Basta substituir o elemento na posição e chamar o item notificarItemChanged (posição);
toshkinl
@toshkinl Como você saberia quando os dados estão vindo do JSONObject? sem posição?
olajide
7

Eu consegui resolver esse problema pegando a posição do item que precisava ser modificado e então na chamada do adaptador

public void refreshBlockOverlay(int position) {
    notifyItemChanged(position);
}

, isso irá chamar onBindViewHolder (titular do ViewHolder, posição interna) para este item específico nesta posição específica.

HaneenCo
fonte
2

A solução abaixo funcionou para mim:

Em um item RecyclerView, o usuário clica em um botão, mas outra visualização como TextView será atualizada sem notificar diretamente o adaptador:

Eu encontrei uma boa solução para isso sem usar o método notificationDataSetChanged (), este método recarrega todos os dados de recyclerView, então se você tiver imagem ou vídeo dentro do item, eles serão recarregados e a experiência do usuário não será boa:

Aqui está um exemplo de clique em um ícone semelhante a ImageView e atualizar apenas uma única TextView (possível atualizar mais visualização da mesma maneira do mesmo item) para mostrar como atualização de contagem após adicionar +1:

// View holder class inside adapter class
public class MyViewHolder extends RecyclerView.ViewHolder{

    ImageView imageViewLike;

    public MyViewHolder(View itemView) {
         super(itemView);

        imageViewLike = itemView.findViewById(R.id.imageViewLike);
        imageViewLike.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int pos = getAdapterPosition(); // Get clicked item position
                TextView tv = v.getRootView().findViewById(R.id.textViewLikeCount); // Find textView of recyclerView item
                resultList.get(pos).setLike(resultList.get(pos).getLike() + 1); // Need to change data list to show updated data again after scrolling
                tv.setText(String.valueOf(resultList.get(pos).getLike())); // Set data to TextView from updated list
            }
        });
    }
Touhid
fonte
1

Uma maneira que funcionou para mim pessoalmente é usar os métodos do adaptador do recyclerview para lidar com as mudanças em seus itens.

Seria de uma forma semelhante a esta, criar um método na visualização do reciclador personalizado mais ou menos assim:

public void modifyItem(final int position, final Model model) {
    mainModel.set(position, model);
    notifyItemChanged(position);
}
user3709410
fonte
0

Em sua classe de adaptador, no método onBindViewHolder, defina ViewHolder como setIsRecyclable (false) como no código abaixo.

@Override
public void onBindViewHolder(RecyclerViewAdapter.ViewHolder p1, int p2)
{
    // TODO: Implement this method
    p1.setIsRecyclable(false);

    // Then your other codes
}
Shankar_vl
fonte
0

você só precisa adicionar o seguinte código na caixa de diálogo de alerta ao clicar em salvar

          recyclerData.add(position, updateText.getText().toString());
          recyclerAdapter.notifyItemChanged(position);
Dhruvisha
fonte
0

O problema é RecyclerView.Adatperque não fornece nenhum método que retorne o índice do elemento

public abstract static class Adapter<VH extends ViewHolder> {
  /**
   * returns index of the given element in adapter, return -1 if not exist
   */
  public int indexOf(Object elem);
}

Minha solução alternativa é criar uma instância de mapa para (elemento, posição) s

public class FriendAdapter extends RecyclerView.Adapter<MyViewHolder> {
  private Map<Friend, Integer> posMap ;
  private List<Friend> friends;

  public FriendAdapter(List<Friend> friends ) {
    this.friends = new ArrayList<>(friends);
    this.posMap = new HashMap<>();
    for(int i = 0; i < this.friends.size(); i++) {
      posMap.put(this.friends.get(i), i);
    }
  }

  public int indexOf(Friend friend) {
    Integer position = this.posMap.get(elem);
    return position == null ? -1 : position;
  }
  // skip other methods in class Adapter
}
  • o tipo de elemento (aqui classe Friend) deve implementar hashCode()e equals()porque é a chave no hashmap.

quando um elemento mudou,

  void someMethod() {
    Friend friend = ...;
    friend.setPhoneNumber('xxxxx');

    int position = friendAdapter.indexOf(friend);
    friendAdapter.notifyItemChanged(position);

  }

É bom definir um método auxiliar

public class FriendAdapter extends extends RecyclerView.Adapter<MyViewHolder> {

  public void friendUpdated(Friend friend) {
    int position = this.indexOf(friend);
    this.notifyItemChanged(position);
  }
}

A instância do mapa ( Map<Friend, Integer> posMap) não é necessariamente necessária. Se o mapa não for usado, o loop em toda a lista pode encontrar a posição de um elemento.

chmin
fonte
-1

Esse também é meu último problema. Aqui minha solução eu uso modelo de dados e adaptador para meu RecyclerView

 /*Firstly, register your new data to your model*/
 DataModel detail = new DataModel(id, name, sat, image);

 /*after that, use set to replace old value with the new one*/
 int index = 4;
 mData.set(index, detail);

 /*finally, refresh your adapter*/
 if(adapter!=null)
    adapter.notifyItemChanged(index);
deya tri
fonte
-1

se você está criando um objeto e adicionando-o à lista que você usa em seu adaptador, quando você muda um elemento de sua lista no adaptador, todos os seus outros itens mudam também em outras palavras, são apenas referências e sua lista não mantenha cópias separadas desse único objeto.

A.sobhdel
fonte
-5

Em seu adaptador RecyclerView, você deve ter um ArrayList e também um método addItemsToList(items)para adicionar itens de lista ao ArrayList. Em seguida, você pode adicionar itens de lista por chamada adapter.addItemsToList(items)dinamicamente. Depois que todos os itens da sua lista forem adicionados ao ArrayList, você pode chamar adapter.notifyDataSetChanged()para exibir sua lista.

Você pode usar o notifyDataSetChangedno adaptador para o RecyclerView

Nooruddin Lakhani
fonte
sim eu tenho ArrayList para dados, funciona sem problemas, meu problema em atualizar item
-existente-
você deve ter a posição do item que está sendo atualizada, então você só tem que colocar o novo item em sua respectiva posição como mList.add (position) .setName ("new name"); em seguida, use mAdapter.notifyDataSetChanged ();
Nooruddin Lakhani,
4
Esta resposta ignora o desejo de atualizar um único item. A chamada de notificationDataSetChanged () só deve ser feita quando todo o conjunto de dados muda.
Mark McClelland