当前位置: 首页 > 工具软件 > Diffutils > 使用案例 >

DiffUtils使用流程

梅安平
2023-12-01
DiffUtils是用来更新RecyclerView的工具,使用DiffUtils可以代替手动刷新RecyclerView。

使用方法

一、确定Item是否更新的规则(数据变化了,但是不一定需要更新UI界面)

创建一个类继承 DiffUtil.Callback ,重新四个abstract方法
class DiffCallBack(private val lastData: ArrayList<TestBean>, private val newData: ArrayList<TestBean>) :
    DiffUtil.Callback() {

    override fun getOldListSize(): Int {
        return lastData.size
    }

    override fun getNewListSize(): Int {
        return newData.size
    }

    /**
     * 用来判断是否是同一个item
     */
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        LogV("oldItemPosition = $oldItemPosition , newItemPosition = $newItemPosition")
        return lastData[oldItemPosition].name == newData[newItemPosition].name
    }

    /**
     * 只有 areItemsTheSame 为 True的时候才会调用这个方法
     * 用来判断是否UI需要改变
     */
    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        LogV("oldItemPosition = $oldItemPosition , newItemPosition = $newItemPosition")
        return lastData[oldItemPosition] == newData[newItemPosition]
    }
}

二、计算新老数据集的差异,并且通知RecyclerView刷新

    //setDatas函数里面就不需要写notifyDataSetChanged()了,在数据更新的时候会重新走onbindView,只要数据是新的就行了。
    mAdapter.setDatas(newData)
    //计算新老数据集的差异
    val diffResult = DiffUtil.calculateDiff(DiffCallBack(oldData, newData), true)
    //通知RecyclerView刷新
    //推测是通过消息机制来刷新的,即使setDatas函数写在dispatchUpdatesTo后面, 也同样可以成功更新UI。
    diffResult.dispatchUpdatesTo(mAdapter)
使用dispatchUpdatesTo通知recyclerView刷新的时候,其实就是通过Adapter的定向刷新方法来更新数据的。
AdapterListUpdateCallback.class

    @Override
    public void onInserted(int position, int count) {
        mAdapter.notifyItemRangeInserted(position, count);
    }

    @Override
    public void onRemoved(int position, int count) {
        mAdapter.notifyItemRangeRemoved(position, count);
    }

    @Override
    public void onMoved(int fromPosition, int toPosition) {
        mAdapter.notifyItemMoved(fromPosition, toPosition);
    }

    @Override
    public void onChanged(int position, int count, Object payload) {
        mAdapter.notifyItemRangeChanged(position, count, payload);
    }

高阶用法,ViewHolder的局部刷新(究极节约资源大法)

比如一个ViewHolder里面有一个TV(TextView)跟一个IV(ImageView),IV 的图片链接没变化,TV的文字变化了,按照往常处理方法更新ViewHolder,通常是重走bindView方法,把ViewHolder里面的所有的View都重新赋值,但是使用DiffUtils的高阶用法之后,就可以只针对TV更新,不为IV重新设置图片链接了。

一 、在继承 DiffUtil.Callback的时候,重写getChangePayload方法

override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
    return super.getChangePayload(oldItemPosition, newItemPosition)
}
getChangePayload方法在areItemsTheSame 返回 true , areContentsTheSame返回false的时候调用,即 :新旧数据对应同一个Item,但是 content发生了变化,需要更新 部分或 全部 的UI。

二 、我们判断 那些 UI 绑定的数据发生变化了,然后记录下来,在返回值里面返回,可以使用Bundle或其它数据类型记录变化的数据。

  public Object getChangePayload(int oldItemPosition, int newItemPosition) {

        TestBean oldBean = mOldDatas.get(oldItemPosition);
        TestBean newBean = mNewDatas.get(newItemPosition);

        Bundle payload = new Bundle();
        if (!oldBean.getDesc().equals(newBean.getDesc())) {
            payload.putString("KEY_DESC", newBean.getDesc());
        }
        if (oldBean.getPic() != newBean.getPic()) {
            payload.putInt("KEY_PIC", newBean.getPic());
        }

        if (payload.size() == 0)//如果没有变化 就传空
            return null;
        return payload;
    }

三、重写RecyclerView里面的onBindViewHolder(DiffVH holder, int position, List payloads)方法,我们在getChangePayload 方法里的返回值就是 这个三参数 onBindViewHolder的第三个参数,我们根据哪些数据发生了变化,只更新对应的View即可,不需要重写给ViewHolder里面的View赋值了。

public void onBindViewHolder(DiffVH holder, int position, List<Object> payloads) {
        if (payloads.isEmpty()) {
            onBindViewHolder(holder, position);
        } else {
            Bundle payload = (Bundle) payloads.get(0);
            TestBean bean = mDatas.get(position);
            for (String key : payload.keySet()) {
                switch (key) {
                    case "KEY_DESC":
                        //这里可以用payload里的数据,不过data也是新的 也可以用
                        holder.tv2.setText(bean.getDesc());
                        break;
                    case "KEY_PIC":
                        holder.iv.setImageResource(payload.getInt(key));
                        break;
                    default:
                        break;
                }
            }
        }
    }

 类似资料: