web-dev-qa-db-fra.com

Comment mettre à jour RecyclerView Adapter Data?

Essayer de comprendre quel est le problème avec la mise à jour de l'adaptateur de RecyclerView.

Après avoir reçu une nouvelle liste de produits, j'ai essayé de:

  1. Mettez à jour le ArrayList à partir du fragment où est créé recyclerView, définissez les nouvelles données sur l'adaptateur, puis appelez adapter.notifyDataSetChanged(); cela n'a pas fonctionné.

  2. Créez un nouvel adaptateur, comme d'autres l'ont fait, et cela a fonctionné pour eux, mais aucun changement pour moi: recyclerView.setAdapter(new RecyclerViewAdapter(newArrayList))

  3. Créez une méthode dans Adapter qui met à jour les données comme suit:

    public void updateData(ArrayList<ViewModel> viewModels) {
       items.clear();
       items.addAll(viewModels);
       notifyDataSetChanged();
    }
    

    J'appelle ensuite cette méthode chaque fois que je souhaite mettre à jour la liste de données; cela n'a pas fonctionné.

  4. Pour vérifier si je peux modifier recyclerView de quelque manière que ce soit, et j'ai essayé de supprimer au moins un élément:

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

    Tout est resté tel quel.

Voici mon adaptateur:

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> implements View.OnClickListener {

    private ArrayList<ViewModel> items;
    private OnItemClickListener onItemClickListener;

    public RecyclerViewAdapter(ArrayList<ViewModel> items) {
        this.items = items;
    }


    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.onItemClickListener = onItemClickListener;
    }

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false);
        v.setOnClickListener(this);
        return new ViewHolder(v);
    }

    public void updateData(ArrayList<ViewModel> viewModels) {
        items.clear();
        items.addAll(viewModels);
        notifyDataSetChanged();
    }
    public void addItem(int position, ViewModel viewModel) {
        items.add(position, viewModel);
        notifyItemInserted(position);
    }

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



    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        ViewModel item = items.get(position);
        holder.title.setText(item.getTitle());
        Picasso.with(holder.image.getContext()).load(item.getImage()).into(holder.image);
        holder.price.setText(item.getPrice());
        holder.credit.setText(item.getCredit());
        holder.description.setText(item.getDescription());

        holder.itemView.setTag(item);
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    @Override
    public void onClick(final View v) {
        // Give some time to the ripple to finish the effect
        if (onItemClickListener != null) {
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    onItemClickListener.onItemClick(v, (ViewModel) v.getTag());
                }
            }, 0);
        }
    }

    protected static class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView image;
        public TextView price, credit, title, description;

        public ViewHolder(View itemView) {
            super(itemView);
            image = (ImageView) itemView.findViewById(R.id.image);
            price = (TextView) itemView.findViewById(R.id.price);
            credit = (TextView) itemView.findViewById(R.id.credit);
            title = (TextView) itemView.findViewById(R.id.title);
            description = (TextView) itemView.findViewById(R.id.description);
        }
    }

    public interface OnItemClickListener {

        void onItemClick(View view, ViewModel viewModel);

    }
}

Et j'initie RecyclerView comme suit:

recyclerView = (RecyclerView) view.findViewById(R.id.recycler);
recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 5));
adapter = new RecyclerViewAdapter(items);
adapter.setOnItemClickListener(this);
recyclerView.setAdapter(adapter);

Alors, comment puis-je mettre à jour les données de l'adaptateur afin d'afficher les éléments récemment reçus?


Mise à jour: le problème était que la mise en page où gridView était ressemblé:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:orientation="vertical"
    Android:layout_width="match_parent"
    Android:tag="catalog_fragment"
    Android:layout_height="match_parent">

    <FrameLayout
        Android:orientation="vertical"
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/recycler"
            Android:layout_width="match_parent"
            Android:layout_height="match_parent"
            Android:clipToPadding="false"/>

        <ImageButton
            Android:id="@+id/fab"
            Android:layout_gravity="top|end"
            style="@style/FabStyle"/>

    </FrameLayout>
</LinearLayout>

Ensuite, je viens de retirer LinearLayout et de transformer FrameLayout en tant que modèle parent.

226
Filip Luchianenco

Je travaille avec RecyclerView et la suppression et la mise à jour fonctionnent bien.

1) SUPPRIMER: Il y a 4 étapes pour supprimer un élément d’un RecyclerView

list.remove(position);
recycler.removeViewAt(position);
mAdapter.notifyItemRemoved(position);                 
mAdapter.notifyItemRangeChanged(position, list.size());

Cette ligne de codes fonctionne pour moi.

2) MISE À JOUR DES DONNÉES: La seule chose que je devais faire est de

mAdapter.notifyDataSetChanged();

Vous avez dû faire tout cela dans le code Actvity/Fragment et non dans le code de l'adaptateur RecyclerView.

281
nicopasso

Ceci est une réponse générale pour les futurs visiteurs. Les différentes manières de mettre à jour les données de l'adaptateur sont expliquées. Le processus comprend à chaque fois deux étapes principales:

  1. Mettre à jour le jeu de données
  2. Notifier l'adaptateur du changement

Insérer un seul élément

Ajoutez "Cochon" à l'index 2.

Insert single item

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

Insérer plusieurs éléments

Insérez trois autres animaux à l'index 2.

Insert multiple items

ArrayList<String> items = new ArrayList<>();
items.add("Pig");
items.add("Chicken");
items.add("Dog");
int insertIndex = 2;
data.addAll(insertIndex, items);
adapter.notifyItemRangeInserted(insertIndex, items.size());

Supprimer un seul élément

Supprimer "Cochon" de la liste.

Remove single item

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

Supprimer plusieurs éléments

Supprimez "Chameau" et "Mouton" de la liste.

Remove multiple items

int startIndex = 2; // inclusive
int endIndex = 4;   // exclusive
int count = endIndex - startIndex; // 2 items will be removed
data.subList(startIndex, endIndex).clear();
adapter.notifyItemRangeRemoved(startIndex, count);

Supprimer tous les éléments

Effacer la liste entière.

Remove all items

data.clear();
adapter.notifyDataSetChanged();

Remplacer l'ancienne liste par une nouvelle liste

Effacez l'ancienne liste puis ajoutez-en une nouvelle.

Replace old list with new list

// clear old list
data.clear();

// add new list
ArrayList<String> newList = new ArrayList<>();
newList.add("Lion");
newList.add("Wolf");
newList.add("Bear");
data.addAll(newList);

// notify adapter
adapter.notifyDataSetChanged();

La adapter a une référence à data, il est donc important que je n’ai pas défini data sur un nouvel objet. Au lieu de cela, j'ai effacé les anciens éléments de data, puis ajouté les nouveaux.

Mettre à jour un élément

Modifiez l'élément "Moutons" pour qu'il indique "J'aime les moutons".

Update single item

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

Déplacer un article

Déplacez "Moutons" de la position 3 à la position 1.

Move single item

int fromPosition = 3;
int toPosition = 1;

// update data array
String item = data.get(fromPosition);
data.remove(fromPosition);
data.add(toPosition, item);

// notify adapter
adapter.notifyItemMoved(fromPosition, toPosition);

Code

Voici le code du projet pour votre référence. Le code de l’adaptateur RecyclerView est disponible à l’adresse cette réponse .

MainActivity.Java

public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener {

    List<String> data;
    MyRecyclerViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // data to populate the RecyclerView with
        data = new ArrayList<>();
        data.add("Horse");
        data.add("Cow");
        data.add("Camel");
        data.add("Sheep");
        data.add("Goat");

        // set up the RecyclerView
        RecyclerView recyclerView = findViewById(R.id.rvAnimals);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
                layoutManager.getOrientation());
        recyclerView.addItemDecoration(dividerItemDecoration);
        adapter = new MyRecyclerViewAdapter(this, data);
        adapter.setClickListener(this);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on row number " + position, Toast.LENGTH_SHORT).show();
    }

    public void onButtonClick(View view) {
        insertSingleItem();
    }

    private void insertSingleItem() {
        String item = "Pig";
        int insertIndex = 2;
        data.add(insertIndex, item);
        adapter.notifyItemInserted(insertIndex);
    }

    private void insertMultipleItems() {
        ArrayList<String> items = new ArrayList<>();
        items.add("Pig");
        items.add("Chicken");
        items.add("Dog");
        int insertIndex = 2;
        data.addAll(insertIndex, items);
        adapter.notifyItemRangeInserted(insertIndex, items.size());
    }

    private void removeSingleItem() {
        int removeIndex = 2;
        data.remove(removeIndex);
        adapter.notifyItemRemoved(removeIndex);
    }

    private void removeMultipleItems() {
        int startIndex = 2; // inclusive
        int endIndex = 4;   // exclusive
        int count = endIndex - startIndex; // 2 items will be removed
        data.subList(startIndex, endIndex).clear();
        adapter.notifyItemRangeRemoved(startIndex, count);
    }

    private void removeAllItems() {
        data.clear();
        adapter.notifyDataSetChanged();
    }

    private void replaceOldListWithNewList() {
        // clear old list
        data.clear();

        // add new list
        ArrayList<String> newList = new ArrayList<>();
        newList.add("Lion");
        newList.add("Wolf");
        newList.add("Bear");
        data.addAll(newList);

        // notify adapter
        adapter.notifyDataSetChanged();
    }

    private void updateSingleItem() {
        String newValue = "I like sheep.";
        int updateIndex = 3;
        data.set(updateIndex, newValue);
        adapter.notifyItemChanged(updateIndex);
    }

    private void moveSingleItem() {
        int fromPosition = 3;
        int toPosition = 1;

        // update data array
        String item = data.get(fromPosition);
        data.remove(fromPosition);
        data.add(toPosition, item);

        // notify adapter
        adapter.notifyItemMoved(fromPosition, toPosition);
    }
}

Remarques

  • Si vous utilisez notifyDataSetChanged(), aucune animation ne sera exécutée. Cela peut également être une opération coûteuse, il est donc déconseillé d'utiliser notifyDataSetChanged() si vous ne mettez à jour qu'un seul élément ou une plage d'éléments.
  • Vérifiez DiffUtil si vous apportez des modifications importantes ou complexes à une liste.

Une étude plus approfondie

224
Suragch

C'est ce qui a fonctionné pour moi:

recyclerView.setAdapter(new RecyclerViewAdapter(newList));
recyclerView.invalidate();

Après avoir créé un nouvel adaptateur contenant la liste mise à jour (dans mon cas, il s'agissait d'une base de données convertie en ArrayList) et l'avoir défini en tant qu'adaptateur, j'ai essayé recyclerView.invalidate() et tout a fonctionné.

41
martinpkmn

vous avez 2 options pour cela: actualiser l'interface utilisateur à partir de l'adaptateur:

mAdapter.notifyDataSetChanged();

ou actualisez-le à partir de recyclerView lui-même:

recyclerView.invalidate();
17
ediBersh

Une autre option consiste à utiliser diffutil . Il comparera la liste d'origine à la nouvelle liste et utilisera la nouvelle liste comme mise à jour en cas de changement.

Fondamentalement, nous pouvons utiliser DiffUtil pour comparer les anciennes données avec les nouvelles données et le laisser appeler notifyItemRangeRemoved, notifyItemRangeChanged et notifyItemRangeInserted en votre nom.

Un exemple rapide d'utilisation de diffUtil au lieu de notifyDataSetChanged:

DiffResult diffResult = DiffUtil
                .calculateDiff(new MyDiffUtilCB(getItems(), items));

//any clear up on memory here and then
diffResult.dispatchUpdatesTo(this);

//and then, if necessary
items.clear()
items.addAll(newItems)

Je fais le calcul calculer en dehors du fil principal au cas où ce serait une grande liste.

10
j2emanue

Mise à jour des données de listview, gridview et recyclerview

mAdapter.notifyDataSetChanged();

ou

mAdapter.notifyItemRangeChanged(0, itemList.size());
8
Pankaj Talaviya

J'ai résolu le même problème d'une manière différente. Je n'ai pas de données que j'attendais du fil d'arrière-plan, alors commencez par une liste emty.

        mAdapter = new ModelAdapter(getContext(),new ArrayList<Model>());
   // then when i get data

        mAdapter.update(response.getModelList());
  //  and update is in my adapter

        public void update(ArrayList<Model> modelList){
            adapterModelList.clear(); 
            for (Product model: modelList) {
                adapterModelList.add(model); 
            }
           mAdapter.notifyDataSetChanged();
        }

C'est ça.

5
Ahmed Ozmaan

Le moyen le plus efficace d’ajouter de nouvelles données aux données actuelles est

 ArrayList<String> newItems = new ArrayList<String>();
 newItems = getList();
 int oldListItemscount = alcontainerDetails.size();
 alcontainerDetails.addAll(newItems);           
 recyclerview.getAdapter().notifyItemChanged(oldListItemscount+1, al_containerDetails);
5
Rushi Ayyappa

Ces méthodes sont efficaces et vous permettent de commencer à utiliser une base RecyclerView.

private List<YourItem> items;

public void setItems(List<YourItem> newItems)
{
    clearItems();
    addItems(newItems);
}

public void addItem(YourItem item, int position)
{
    if (position > items.size()) return;

    items.add(item);
    notifyItemInserted(position);
}

public void addMoreItems(List<YourItem> newItems)
{
    int position = items.size() + 1;
    newItems.addAll(newItems);
    notifyItemChanged(position, newItems);
}

public void addItems(List<YourItem> newItems)
{
    items.addAll(newItems);
    notifyDataSetChanged();
}

public void clearItems()
{
    items.clear();
    notifyDataSetChanged();
}

public void addLoader()
{
    items.add(null);
    notifyItemInserted(items.size() - 1);
}

public void removeLoader()
{
    items.remove(items.size() - 1);
    notifyItemRemoved(items.size());
}

public void removeItem(int position)
{
    if (position >= items.size()) return;

    items.remove(position);
    notifyItemRemoved(position);
}

public void swapItems(int positionA, int positionB)
{
    if (positionA > items.size()) return;
    if (positionB > items.size()) return;

    YourItem firstItem = items.get(positionA);

    videoList.set(positionA, items.get(positionB));
    videoList.set(positionB, firstItem);

    notifyDataSetChanged();
}

Vous pouvez les implémenter dans une classe d'adaptateur ou dans votre fragment ou activité, mais dans ce cas, vous devez instancier l'adaptateur pour appeler les méthodes de notification. Dans mon cas, je l'implémente généralement dans l'adaptateur.

2
Teocci

J'ai découvert qu'un moyen très simple de recharger le RecyclerView consiste simplement à appeler

recyclerView.removeAllViews();

Cela supprimera d'abord tout le contenu de RecyclerView, puis l'ajoutera à nouveau avec les valeurs mises à jour.

2
Jonathan Persgarden

Si rien de ce qui est mentionné dans les commentaires ci-dessus ne fonctionne pour vous. Cela pourrait signifier que le problème se situe ailleurs.

À un endroit où j'ai trouvé la solution, j'ai créé la liste avec l'adaptateur. Dans mon activité, la liste était une variable d'instance et je la changeais directement lorsque des données étaient modifiées. Comme il s'agissait d'une variable de référence, il se passait quelque chose d'étrange. J'ai donc remplacé la variable de référence par une variable locale et utilisé une autre variable pour mettre à jour les données, puis passer à la fonction addAll() mentionnée dans les réponses ci-dessus.

1
bitSmith

j'ai eu la réponse après un long moment

  SELECTEDROW.add(dt);
                notifyItemInserted(position);
                SELECTEDROW.remove(position);
                notifyItemRemoved(position);
1
roufal