如何使用 DiffUtils 更新 RecyclerView 中的列表?
Posted
技术标签:
【中文标题】如何使用 DiffUtils 更新 RecyclerView 中的列表?【英文标题】:How to update list in RecyclerView with DiffUtils? 【发布时间】:2020-01-16 21:34:08 【问题描述】:在使用 DiffUtil ItemCallback 时,如何使用 DiffCallback 在 RecyclerView 中加载 newList。
当用户选择我希望 RecyclerView 更新的不同尺寸时,我想为用户提供从数据库返回不同尺寸列表的选项。
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) ...
我希望 RecycelView 在用户更改列表大小时自动更新。如何从 ListAdapter 调用旧列表,甚至会更新 RecyclerView
【问题讨论】:
你在上面的代码中遇到了什么问题? 如何从 ArrayListAdapter 调用列表? 您不必自己计算差异。只需将新列表传递给适配器adapter.submitList(newList)
,适配器就会接受它,在后台线程上计算差异并使用动画更新列表
@sonnet 我也在想同样的事情,但我收到以下错误 androidx.room.RoomTrackingLiveData 无法转换为 java.util.List。当我尝试从我的 ViewModel 调用 getList() 时。它是一个 liveData>.
这是一个单独的问题。将您的实时数据从 dao 返回到 viewmodel,然后返回到片段/活动。在您的片段/活动中观察此实时数据
【参考方案1】:
您可以在 youtube 中创建一个模板,就像此视频中显示的那样:
https://www.youtube.com/watch?v=y31fzLe2Ajw
这是一个适配器的例子。
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);
在 MainActivity 你这样调用适配器:
CartFragAdapter adapter = new CartFragAdapter();
adapter.submitList(inputData);
希望对你有帮助!
【讨论】:
【参考方案2】:观察这段代码
这是我的 diffutil 回调
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);
这是使用 diffutilcallback 的 recyclerview adpater
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);
在这里我可以更新按钮点击事件中的活动列表
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
);
用你的模型类替换字符串列表
【讨论】:
【参考方案3】:并且除了上述(使用DiffUtils)之外,如果你使用Clicks来改变RecyclerView的内容,那么
ViewHolder implements View.OnClickListener
并使用它在 onBindViewHolder 中设置 OnClickListener
.setOnClickListener(holder);
得到你当前的位置
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();
早期我在 onBindViewHolder 中使用了 final int 位置,不明白出了什么问题...)))
【讨论】:
以上是关于如何使用 DiffUtils 更新 RecyclerView 中的列表?的主要内容,如果未能解决你的问题,请参考以下文章
Android开发Diffutils打造不一样的recyclerview
如何在 Recyclerview 中正确使用 DiffUtil 更新数组列表?
DiffUtils 遇到 Kotlin,榨干视图局部刷新的最后一滴性能
在适配器 kotlin 中更新视图时如何正确使用 Diff utils