Android RecyclerView DiffUtil 局部刷新 整理 踩坑 封装

最近有点空闲 将最近使用的DiffUtil局部刷新进行了一个整理
封装了一个BaseDiffAdapter,一个类搞定
一些坑基本也踩完了,什么图片闪烁、item闪烁、数据源刷新view不刷新等等

Image discription

放段代码 欢迎拍砖

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<T> 进行操作完后 请勿使用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();
        }
    }

源码链接

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值