我的列表中有200多个项目。 RecyclerView定期更新(每10秒钟)。 RecyclerView在更新期间阻止ui线程几秒钟。我正在使用notifyDataSetChanged
方法进行刷新回收查看。还有另一种防止冻结的方法吗?顺便说一下,我不想使用分页。
此方法每10秒运行一次:
public void refreshAssetList(List<Asset> newAssetList){
recyclerViewAdapter.setAssetList(newAssetList);
recyclerViewAdapter.notifyDataSetChanged();
}
RecyclerViewAdapter类:
public class AssetListRecyclerViewAdapter extends RecyclerView.Adapter<AssetListRecyclerViewAdapter.BaseViewHolder> {
private List<Asset> assetList;
Context context;
public AssetListRecyclerViewAdapter(List<Asset> assetList, Context context) {
this.assetList = assetList;
this.context = context;
}
public void setAssetList(List<Asset> assetList) {
this.assetList = assetList;
}
@Override
public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_asset, null);
return new DetailViewHolder(itemLayoutView);
}
@Override
public void onBindViewHolder(BaseViewHolder holder, int position) {
Asset asset = assetList.get(position);
Last last = asset.getLast();
if (holder.getItemViewType() == TYPE_DETAIL) {
DetailViewHolder mHolder = (DetailViewHolder) holder;
mHolder.dateTextView.setText(last.getDate());
mHolder.brandTextView.setText(asset.getMc());
}
}
class DetailViewHolder extends BaseViewHolder {
@Bind(R.id.brandTextV)
TextView brandTextView;
@Bind(R.id.dateTextV)
TextView dateTextView;
DetailViewHolder(View itemLayoutView) {
super(itemLayoutView);
ButterKnife.bind(this, itemLayoutView);
}
}
}
答案 0 :(得分:2)
您无需致电notifyDataSetChanged
,这是一项昂贵的操作,您的整个RecyclerView
将完全重绘,重新绑定等。
正如文件所说:
此事件未指定数据集的更改内容, 强迫任何观察者假设所有现有的物品和结构 可能不再有效。 LayoutManagers将被迫完全重新绑定 并重新布局所有可见的视图。
您需要做的就是遍历每个位置,如果需要,请更新所需的项目,否则不执行任何操作或跳过。
你应该做什么:
当您首先更新整个视图时,您需要将您的(可见)适配器List<Asset>
与新List<Asset>
进行比较,并仅检索您需要更新的项目,让列表循环更新列表并使用viewAdapter.notifyItemChanged(position)
更新您的适配器视图。
答案 1 :(得分:1)
执行操作以将适配器更新为:
public void refreshAssetList(List<Asset> newAssetList){
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
recyclerViewAdapter.setAssetList(newAssetList);
recyclerViewAdapter.notifyDataSetChanged();
}
});
}
答案 2 :(得分:0)
经过一番研究后,我找到了用于更新列表的DiffUtil类。
来自文档:
DiffUtil是一个可以计算它们之间差异的实用程序类 两个列表并输出一个转换更新操作的列表 第一个列入第二个。
DiffUtil需要新列表和旧列表。它仅更新列表中的更改项目。我创建了AssetDiffUtil类,如下所示:
public class AssetDiffUtil extends DiffUtil.Callback {
private final List<Asset> oldList;
private final List<Asset> newList;
public AssetDiffUtil(List<Asset> oldList, List<Asset> 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 oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).getId() == newList.get(newItemPosition).getId();
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
final Last oldItem = oldList.get(oldItemPosition).getLast();
final Last newItem = newList.get(newItemPosition).getLast();
return oldItem.getDate().equals(newItem.getDate());
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
// Implement method if you're going to use ItemAnimator
return super.getChangePayload(oldItemPosition, newItemPosition);
}
}
然后我在AssetListRecyclerViewAdapter类中添加了swapItems方法来刷新列表。
public void swapItems(List<Asset> newAssetList) {
final DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new AssetDiffUtil(this.assetList, newAssetList));
this.assetList.clear();
this.assetList.addAll(newAssetList);
diffResult.dispatchUpdatesTo(this);
}
就是这样。但我的问题仍然存在。在使用DiffUtil之前冻结时间为4秒。现在,使用DiffUtil后冻结时间是2秒。不幸的是,这不是我的问题的最终解决方案。
答案 3 :(得分:0)
使用 notifyItemRangeChanged 而不是 notifyDataSetChanged ,有时在使用 SpinKitView 等进度条库时也会增加冻结时间
loadMoreProgress.visibility = View.VISIBLE
homeViewModel.latestBars.observe(viewLifecycleOwner, Observer {
latestBarTests.addAll(it)
latestBarsAdapter.notifyItemRangeChanged(
latestBarTests.size - it.size,
latestBarTests.size
)
})