'How to update list in RecyclerView with DiffUtils?

How do you use DiffCallback to load a newList in RecyclerView when DiffUtil ItemCallback is being used.

I would like to give the user the option to return different size lists from the database when the user selects a different size I want the RecyclerView to update.

RecyclerViewAdatper

RecyclerViewAdapter extends ListAdapter<WordEntity, RecyclerViewAdapter.ViewHolder> {
private RecyclerViewAdapter() {
    super(DIFF_CALLBACK);
}

private static final DiffUtil.ItemCallback<WordEntity> DIFF_CALLBACK = new DiffUtil.ItemCallback<WordEntiti>() {

    @Override
    public boolean areItemsTheSame...

    @Override
    public boolean areContentsTheSame...
};

@Override
public viewHolder onCreateViewHolder...

@Override
public void onVindViewHolder ...

class ViewHolder extends RecyclerView.ViewHolder ...

public void updateWordList(List<WordEntity> words) {
    final WordDiffCallBack diffCallBack = new WordDiffCallBack(list???, words);
    final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffCallBack);

    this.list???.clear();
    this.addAll(words);
    diffResult.dispatcheUpdatesTo(this);
}

WordsDiffCallBack

private final List<WordEntity> mOldList;
private final List<WordEntity> mNewList;

public WordsDiffCallBack(List<WordEntity> oldList, List<WordEntity> newList) {
    this.mOldList = oldList;
    this.mNewList = newList;
}

@Override
public int getOldListSize() {
    return mOldList.size();
}

@Override
public int getNewListSize() {
    return mNewList.size();
}

@Override
public boolean areItemsTheSame(int OldItemPostion, int newItemPosition) ...

@Override boolean areContentsTheSame(int oldItemPosition, int newItemPosition)...

@Override getChangePayload(int oldItemPosition, int newItemPosition) ...
}

I want the RecycelView to update automatically when the size of the list gets changed by the user. How do I call the old list from the ListAdapter and will that even update the RecyclerView



Solution 1:[1]

You can create a template just like shown in this video in youTube:

https://www.youtube.com/watch?v=y31fzLe2Ajw

Here is an example of adapter.

public class CartFragAdapter extends RecyclerView.Adapter<CartFragAdapter.CartFragViewHolder> {

    private static final String TAG = "debinf PurchaseAdap";

    private static final DiffUtil.ItemCallback<ProductsObject> DIFF_CALLBACK = new DiffUtil.ItemCallback<ProductsObject>() {
        @Override
        public boolean areItemsTheSame(@NonNull ProductsObject oldProduct, @NonNull ProductsObject newProduct) {
            Log.i(TAG, "areItemsTheSame: old is "+oldProduct.getCode()+" ; new is "+newProduct.getCode());
            return oldProduct.getCode().equals(newProduct.getCode());
        }

        @Override
        public boolean areContentsTheSame(@NonNull ProductsObject oldProduct, @NonNull ProductsObject newProduct) {
            Log.i(TAG, "areContentsTheSame: old is "+oldProduct.getPrice()+" ; new is "+newProduct.getPrice());
            return oldProduct.getPrice() == newProduct.getPrice();
        }
    };

    private AsyncListDiffer<ProductsObject> differ = new AsyncListDiffer<ProductsObject>(this, DIFF_CALLBACK);

    @NonNull
    @Override
    public CartFragViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_purchase, parent, false);
        return new CartFragViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull CartFragViewHolder holder, int position) {
        final ProductsObject purchaseList = differ.getCurrentList().get(position);

        holder.mCode.setText(purchaseList.getCode());
        holder.mPrice.setText(String.valueOf(purchaseList.getPrice()));
        holder.mDescription.setText(purchaseList.getDescription());


    }

    @Override
    public int getItemCount() {
        Log.i(TAG, "getItemCount");
        return differ.getCurrentList().size();
    }

    public void submitList(List<ProductsObject> products){
        Log.i(TAG, "submitList: products.size is "+products.size());
        differ.submitList(products);
    }

    public class CartFragViewHolder extends RecyclerView.ViewHolder {

        public TextView mCode, mPrice, mDescription;

        public CartFragViewHolder(@NonNull View itemView) {
            super(itemView);

            mCode = (TextView) itemView.findViewById(R.id.item_productCode);
            mPrice = (TextView) itemView.findViewById(R.id.item_productPrice);
            mDescription = (TextView) itemView.findViewById(R.id.item_productDescription);
        }
    }
}

In MainActivity you call adapter like this:

CartFragAdapter adapter = new CartFragAdapter();
adapter.submitList(inputData);

I hope it helps!

Solution 2:[2]

observe this code

here's my diffutil callback

public class MyDiffUtilCallback extends DiffUtil.Callback {

List<String> oldlist;
List<String > newlist;

public MyDiffUtilCallback(List<String> oldlist, List<String> newlist) {
    this.oldlist = oldlist;
    this.newlist = newlist;
}
@Override
public int getOldListSize() {
    return oldlist.size();
}
@Override
public int getNewListSize() {
    return newlist.size();
}
@Override
public boolean areItemsTheSame(int olditempostion, int newitemPostion) {
    return olditempostion==newitemPostion;
}
@Override
public boolean areContentsTheSame(int olditempostion, int newitemPostion) {
    return olditempostion==newitemPostion;
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
    return super.getChangePayload(oldItemPosition, newItemPosition);
}

}

here's the recyclerview adpater which uses diffutilcallback

public class recyclerviewAdapter extends RecyclerView.Adapter<recyclerviewAdapter.Viewholder> {

List<String> datasource;

public recyclerviewAdapter(List<String> datasource) {
    this.datasource = datasource;
}

@Override
public Viewholder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) {
    View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.demorow,viewGroup,false);
    return new Viewholder(itemView);
}

@Override
public void onBindViewHolder(@NonNull Viewholder viewholder, int i) {
    viewholder.tv_demo_data_row.setText(datasource.get(i));
}

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

public static class Viewholder extends RecyclerView.ViewHolder {

    TextView tv_demo_data_row;

    public Viewholder(@NonNull View itemView) {
        super(itemView);

        tv_demo_data_row=itemView.findViewById(R.id.tv_demo_data_row);
    }
}

//DIFF CALLBACK STATE
public void insertdata(List<String> insertList){
    /**
     * Insert list insert data to list
     */
    MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,insertList);
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
    datasource.addAll(insertList);
    diffResult.dispatchUpdatesTo(this);
}

public void updateList(List<String> newList){
    /**
     * update list clear old data and update new data
     */
    MyDiffUtilCallback diffUtilCallback = new MyDiffUtilCallback(datasource,newList);
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(diffUtilCallback);
    datasource.clear();
    datasource.addAll(newList);
    diffResult.dispatchUpdatesTo(this);
}}

and here i can update list in activity on button clickevent

insert_data.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            //inserting the data
            List<String> insertlist = new ArrayList<>(); //asign old list
            for(int i=0;i<2;i++){
                insertlist.add(UUID.randomUUID().toString()); // insert new list
                adapter.insertdata(insertlist);
                faster_recyclerview.smoothScrollToPosition(adapter.getItemCount()-1); //auto scroll to last postion
            }
        }
    });

replace List of string with your modelclass

Solution 3:[3]

And in addition to the above (using DiffUtils), if you use Clicks to change RecyclerView's content, make

ViewHolder implements View.OnClickListener

and use this to set OnClickListener in onBindViewHolder

.setOnClickListener(holder);

and get your current position by

this.getAdapterPosition()
private class HistoryRecyclerAdapter extends RecyclerView.Adapter<HistoryRecyclerAdapter.ViewHolder> {
        private List<HistoryEntry> entries;

        HistoryRecyclerAdapter(List<HistoryEntry> entries) {
            this.entries = entries;
        }

        public void updateContainer(List<HistoryEntry> list){
            DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
                @Override
                public int getOldListSize() { return entries.size(); }

                @Override
                public int getNewListSize() { return list.size(); }

                @Override
                public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
                    return entries.get(oldItemPosition).getId()==list.get(newItemPosition).getId();
                }

                @Override
                public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
                    HistoryEntry old = entries.get(oldItemPosition);
                    HistoryEntry neww = list.get(newItemPosition);
                    return  old.getExpression().equals(neww.getExpression())
                            && old.getResult().equals(neww.getResult());
                }
            }, false);//detectMoves=true ????? ???????? ??????? ???????? ???? ?????? ?????? ???? ??????????
            entries = list;
            diffResult.dispatchUpdatesTo(this);
        }

        class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
            //some Views
            final Button delBtn;

            ViewHolder(View view){
                super(view);
                //some Views
                delBtn = (Button) view.findViewById(R.id.delete_btn);
            }

            @Override
            public void onClick(View v) {
                switch (v.getId()){
                    //some cases
                    case R.id.delete_btn:
                        historyManager.delete(entries.get(this.getAdapterPosition()));
                        break;
                }
            }
        }

        @NonNull
        @Override
        public HistoryRecyclerAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.history_row, parent, false);
            return new HistoryRecyclerAdapter.ViewHolder(view);
        }

        @Override
        public void onBindViewHolder(HistoryRecyclerAdapter.ViewHolder holder, final int position) {
            //some code
            holder.delBtn.setOnClickListener(holder);
        }

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

    }

Early I used final int position in onBindViewHolder and don't understand what's wrong...)))

Solution 4:[4]

Here is snapet of my project (How to view list of my providers):

public class ProviderAdapter extends ListAdapter<Provider, RecyclerView.ViewHolder> {
   
    private final GeneralListener<Provider> listener;

    public ProviderAdapter(GeneralListener<Provider> listener) {
        super(DIFF_CALLBACK);
        this.listener = listener;
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewHolder(ItemProviderBinding.inflate(LayoutInflater.from(parent.getContext()), parent, false));
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        ((ViewHolder) holder).bind(getItem(position));
    }

    class ViewHolder extends RecyclerView.ViewHolder {
        ItemProviderBinding binding;

        public ViewHolder(ItemProviderBinding binding) {
            super(binding.getRoot());
            this.binding = binding;
        }

        void bind(Provider data) {
            binding.image.setImageURI(data.img_url);

            binding.txtName.setText(data.name);
            binding.txtMealTypes.setText(data.meal_types);
            binding.txtAddress.setText(data.address);

            binding.btnBack.setOnClickListener(view -> {
                listener.onClick(Actions.VIEW, data);
            });
        }
    }


    private static final DiffUtil.ItemCallback<Provider> DIFF_CALLBACK = new DiffUtil.ItemCallback<Provider>() {
        @Override
        public boolean areItemsTheSame(@NonNull Provider oldItem, @NonNull Provider newItem) {
            return oldItem.id == newItem.id;
        }

        @Override
        public boolean areContentsTheSame(@NonNull Provider oldItem, @NonNull Provider newItem) {
            return oldItem.name.equals(newItem.name)
                    && oldItem.meal_types.equals(newItem.meal_types)
                    && oldItem.address.equals(newItem.address);

        }
    };
}

To use it:

ProviderAdapter adapter = new ProviderAdapter(new GeneralListener<Provider>() {
   @Override
   public void onClick(Actions action, Provider provider) {
      if (action == Actions.VIEW) {
          // TODO: View provider details

      }
  }
});
recyclerView.setAdapter(adapter);
adapter.submitList(providerList);

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Aliton Oliveira
Solution 2 Black mamba
Solution 3 Ermac
Solution 4 Mohammed Alomair