Android(安卓)RecyclerView DiffUtil 局部刷新 整理 踩坑 封装
16lz
2021-01-26
最近有点空闲 将最近使用的DiffUtil局部刷新进行了一个整理
封装了一个BaseDiffAdapter,一个类搞定
一些坑基本也踩完了,什么图片闪烁、item闪烁、数据源刷新view不刷新等等
放段代码 欢迎拍砖
package com.example.basediffadapter;import android.app.Activity;import android.os.Handler;import android.os.Looper;import android.support.annotation.LayoutRes;import android.support.annotation.NonNull;import android.support.annotation.Nullable;import android.support.v7.util.DiffUtil;import android.support.v7.widget.RecyclerView;import android.view.View;import android.view.ViewGroup;import java.util.ArrayList;import java.util.List;/** * 高级自带Diff局部刷新功能Adapter * 在对List 进行操作完后 请勿使用notifyDataSetChanged()方法 * 严格调用 setData addData removeData getData updateData 等方法 * Project: ProjectBaseDiffAdapter * Author: LiShen * Time: 2018/9/11 15:16 */ public abstract class BaseDiffAdapter<T extends BaseDiffBean, V extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<V> { private Activity activity; private Class<T> tClass; private Handler mainHandler = new Handler(Looper.getMainLooper()); private List<T> oldData = new ArrayList<>();// 用于新旧对比的旧数据 private List<T> data = new ArrayList<>();// 当前数据 private OnItemClickListener<T> onItemClickListener; private boolean canNotRefresh = false; public BaseDiffAdapter(Activity activity, Class<T> tClass) { this.activity = activity; this.tClass = tClass; } /** * 正常数据绑定 * * @param holder * @param position */ public abstract void bindViewAndData(V holder, int position); /** * 局部数据绑定 * * @param holder * @param position * @param newBean */ public abstract void partBindViewAndData(V holder, int position, T newBean); @Override public abstract V onCreateViewHolder(ViewGroup parent, int viewType); @Override public final void onBindViewHolder(V holder, int position) { bindViewAndData(holder, position); } @Override public final void onBindViewHolder(@NonNull V holder, int position, @NonNull List<Object> payloads) { if (payloads.isEmpty()) { onBindViewHolder(holder, position); } else { partBindViewAndData(holder, position, (T) payloads.get(0)); } } @Override public int getItemCount() { return data.size(); } public void setData(List<T> outData) { setData(outData, null); } /** * 更新数据 核心方法 每次传入新数据时 将列表 data 和 oldData 用 * DiffUtil进行对比(子线程)再将结果 dispatchUpdatesTo 到 * adapter 上 进行界面刷新 (主线程)最后 再将 data 序列化成 新的 * oldData (子线程)以备下次对比 * * * @param outData * @param callback */ public void setData(List<T> outData, final OnSetDataFinishCallback callback) { if (canNotRefresh) { return; } canNotRefresh = true; data.clear(); if (outData != null) { data.addAll(outData); } WorkThreadHelper.get().execute(new Runnable() { @Override public void run() { // 子线程计算差异 final DiffUtil.DiffResult result = DiffUtil.calculateDiff( new BaseDiffCallback( new ArrayList<>(oldData), new ArrayList<>(data))); mainHandler.post(new Runnable() { @Override public void run() { // dispatch 到 adapter 进行界面刷新 result.dispatchUpdatesTo(BaseDiffAdapter.this); } }); oldData.clear(); for (T t : data) { // 子线程序列化生成新列表 oldData.add(JSON.parseObject(JSON.toJSONString(t), tClass)); } canNotRefresh = false; // 如果有回调 if (callback != null) { mainHandler.post(new Runnable() { @Override public void run() { callback.onFinish(); } }); } } }); } /** * 加数据 * * @param t */ public void addData(T t) { if (t != null) { List<T> temp = new ArrayList<>(data); temp.add(t); setData(temp); } } /** * 加数据 * * @param index * @param t */ public void addData(int index, T t) { if (t != null) { List<T> temp = new ArrayList<>(data); temp.add(index, t); setData(temp); } } /** * 加一批数据 * * @param list */ public void addData(List<T> list) { if (list != null) { List<T> temp = new ArrayList<>(data); temp.addAll(list); setData(temp); } } /** * 删数据 * * @param position */ public void removeData(int position) { List<T> temp = new ArrayList<>(data); temp.remove(position); setData(temp); } /** * 删数据 * * @param id */ public void removeData(String id) { int flag = -1; for (int i = 0; i < data.size(); i++) { if (data.get(i).getDiffId().equals(id)) { flag = i; break; } } if (flag >= 0) { removeData(flag); } } /** * 删数据 * * @param t */ public void removeData(T t) { List<T> temp = new ArrayList<>(data); temp.remove(t); setData(temp); } /** * 清除 */ public void clearData() { List<T> temp = new ArrayList<>(data); temp.clear(); setData(temp); } /** * 修改完 {@link #data} 之后 调用此方法 */ public void notifyDataUpdated() { setData(new ArrayList<>(getData())); } public List<T> getData() { return data; } public T getItemData(int position) { return data.get(position); } public View inflater(@LayoutRes int layoutId) { return getActivity().getLayoutInflater().inflate(layoutId, null); } public Activity getActivity() { return activity; } /** * Item被点击了 * * @param holder * @return */ public int onViewHolderItemClick(RecyclerView.ViewHolder holder) { int position = holder.getLayoutPosition(); if (position >= 0 && position < getData().size()) { if (getOnItemClickListener() != null) { getOnItemClickListener().onItemClick(position, getItemData(position)); } } return position; } public interface OnItemClickListener<T> { void onItemClick(int position, T t); } public void setOnItemClickListener(OnItemClickListener<T> onItemClickListener) { this.onItemClickListener = onItemClickListener; } public OnItemClickListener<T> getOnItemClickListener() { return onItemClickListener; } private class BaseDiffCallback extends DiffUtil.Callback { private List<T> oldList; private List<T> newList; private BaseDiffCallback(List<T> oldList, List<T> newList) { this.oldList = oldList; this.newList = newList; } @Override public int getOldListSize() { return oldList != null ? oldList.size() : 0; } @Override public int getNewListSize() { return newList != null ? newList.size() : 0; } @Override public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { return oldList.get(oldItemPosition).getDiffId().equals( newList.get(newItemPosition).getDiffId()); } @Override public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { return oldList.get(oldItemPosition).getDiffContent().equals( newList.get(newItemPosition).getDiffContent()); } @Nullable @Override public Object getChangePayload(int oldItemPosition, int newItemPosition) { T newBean = newList.get(newItemPosition); if (!areContentsTheSame(oldItemPosition, newItemPosition)) { return newBean; } return null; } } public interface OnSetDataFinishCallback { void onFinish(); }}
使用:
先写一个实体类实现BaseDiffBean
public class DemoBean implements BaseDiffBean, Comparable<DemoBean> { public int id; @DrawableRes public int icon; public String content; public void generateData() { // 非核心代码 无视 id = DemoUtil.generateId(); icon = DemoUtil.generateRandomIcon(); content = DemoUtil.generateRandomString(); } /** * 这个方法是为了对比两个类 数据 是否一模一样 **/ @NonNull @Override public String getDiffContent() { return "DemoBean{" + "id=" + id + ", icon=" + icon + ", content='" + content + '\'' + '}'; } /** * 对比两个对象是不是一个 **/ @NonNull @Override public String getDiffId() { return String.valueOf(id); } // 非核心代码 无视 @Override public int compareTo(DemoBean o) { return (int) (this.id - o.id); }}
再写个adapter继承BaseDiffAdapter
public class DemoAdapter extends BaseDiffAdapter<DemoBean, DemoAdapter.ViewHolder> { public DemoAdapter(Activity activity, Class<DemoBean> demoBeanClass) { super(activity, demoBeanClass); setHasStableIds(true); } @Override public void bindViewAndData(final ViewHolder holder, int position) { Log.i("BaseDiffAdapter", "bindViewAndData: " + position); // 正常数据绑定 DemoBean data = getItemData(position); // 配合 setHasStableIds getItemId 防止 图片闪烁 Integer cache = (Integer) holder.ivItemDemo.getTag(R.id.ivItemDemo); if (cache == null || cache != data.icon) { holder.ivItemDemo.setImageResource(data.icon); holder.ivItemDemo.setTag(R.id.ivItemDemo, data.icon); } holder.tvItemDemo.setText(data.content); holder.tvItemDemoId.setText("ID: " + data.id); holder.ivItemDemoDelete.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { int index = holder.getAdapterPosition(); removeData(index);// 删除数据 } }); } @Override public void partBindViewAndData(ViewHolder holder, int position, DemoBean newBean) { Log.d("BaseDiffAdapter", "partBindViewAndData: " + position); // 局部数据刷新 业务中可能不会每个界面元素都会刷新 Integer cache = (Integer) holder.ivItemDemo.getTag(R.id.ivItemDemo); if (cache == null || cache != newBean.icon) { holder.ivItemDemo.setImageResource(newBean.icon); holder.ivItemDemo.setTag(R.id.ivItemDemo, newBean.icon); } holder.tvItemDemo.setText(newBean.content); } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { return new ViewHolder(inflater(R.layout.item_demo)); } @Override public long getItemId(int position) { return getItemData(position).id; } class ViewHolder extends RecyclerView.ViewHolder { private ImageView ivItemDemo; private ImageView ivItemDemoDelete; private TextView tvItemDemo; private TextView tvItemDemoId; ViewHolder(@NonNull View itemView) { super(itemView); ivItemDemo = itemView.findViewById(R.id.ivItemDemo); ivItemDemoDelete = itemView.findViewById(R.id.ivItemDemoDelete); tvItemDemo = itemView.findViewById(R.id.tvItemDemo); tvItemDemoId = itemView.findViewById(R.id.tvItemDemoId); } }}
/** * 增加数据 */ private void addData() { DemoBean d = new DemoBean(); d.generateData(); demoAdapter.addData(0, d);// 增加数据 头部插入 // demoAdapter.addData(d);// 尾部插入 // 非核心代码 mainHandler.postDelayed(new Runnable() { @Override public void run() { RecyclerView rvDemo = findViewById(R.id.rvDemo); rvDemo.smoothScrollToPosition(0); } }, 100); } /** * 随机改变某一项item的数据 */ private void refreshData() { int max = demoAdapter.getItemCount(); if (max > 0) { Random random = new Random(); int chosen = random.nextInt(max - 1); // 更改数据 DemoBean itemData = demoAdapter.getItemData(chosen); itemData.content = DemoUtil.generateRandomString(); itemData.icon = DemoUtil.generateRandomIcon(); // 重点!此处不能调用 notifyDataSetChanged demoAdapter.notifyDataUpdated(); } }
源码链接
更多相关文章
- 一句话锁定MySQL数据占用元凶
- Android(安卓)Room 增删改查 个人经验
- Android(安卓)自定义ContentProvider简单实例
- Android开发:使用AudioTrack播放PCM音频数据【附源码】
- Android(安卓)SQLiteOpenHelper
- Android获取assets文件夹中的数据并写入SD卡示例
- HttpClient 多线程
- Android(安卓)LiveData 使用详解
- android 圆形ListView实现,并附带圆角ImageView