关于RecyclerView#DiffUtil一点总结(一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013762572/article/details/82712205

前言

最近在项目中遇到一个需求,在加载首页列表信息时,先是要加载缓存内容[写在文件中],如果网络请求有数据,则替换缓存内容,并显示新的内容。需求本来很简单,直接一个RecyclerView解决了,写完代码就提交了,比如这样:

这里写图片描述

请原谅数据上的美女图,公司的项目就是如此。其实缓存中的数据与新数据,只有第三条发生了改变,其他的都一毛一样;而且你在刷新的时候,会发现一道白光闪过,有人抱怨体验不太好,那好,我改改,改完之后,变成这个样子了:

这里写图片描述

左边是不太讲究的直接RecyclewView.Adapter.notifyItemRangeChanged()的效果,右边的是使用了android.support.v7.util.DiffUtil这个帮助类实现的效果,可以说,差距还是比较大,那么今天就好好说一下这个DiffUtil的作用。

说明

这个DiffUtil存在的意义,是专门为RecyclerView更新item设计的。回想一下,我们当初从ListView中,使用BaseAdapter.notifyDataChanged()无脑全屏刷新,到RecyclerView.Adapter提供了notifyItem**系列,似的RecyclerView刷新时,可以精确到具体的目标item;最后到使用DiffUtil.DiffResult可以精确到某个Item上某个具体的TextView,或者ImageView的刷新。一步一步走来,你会发现,我们可以控制刷新的可视范围越来越精确,进而会使我们的app每次刷新消耗的资源越来越小,效果越来越nice。扯了这么多,还是还来看看传说中的DiffUtil的使用方法吧。

使用

首先我们定义User对象,作为每个RecyclerView 条目的数据载体:

public class User {
    // 用户Id
    public int id ;
    // 用户名称
    public String name;
    //用户图像
    public String url ;
    //用户年龄
    public int age;
}

定义一个Callback继承DiffUtil.Callback,这个比较重要:

public class UserItemDiffCallBack extends DiffUtil.Callback {

    //旧的数据集合
    private List<User> mOldUserList;
    //新的数据集合
    private List<User> mNewUserList;

    //构造方法 传入旧的数据结构和新的数据结构
    public UserItemDiffCallBack(List<User> oldUserList, List<User> newUserList) {
        this.mOldUserList = oldUserList;
        this.mNewUserList = newUserList;
    }

    //获取旧的数据量大小
    @Override
    public int getOldListSize() {
        return null == mOldUserList ? 0 : mOldUserList.size();
    }

    //获取新的数据量大小
    @Override
    public int getNewListSize() {
        return null == mNewUserList ? 0 : mNewUserList.size();
    }

    //判断两个条目是否是一致的
    //在真实的项目中,我们一般使用id或者index搜索来判断两条item是否一致
    //如果我们的id一样,在系统里面我就认为两个数据记录是一样的
    @Override
    public boolean areItemsTheSame(int oldPosition, int newPosition) {
        return mOldUserList.get(oldPosition).id == mNewUserList.get(newPosition).id;
    }

    //这个需要areItemsTheSame 返回true时才调用
    //即使我们的id是一致的,我们在系统中是同一个对象,但是的name可能更新,或者图像可能更新了
    //这里可以填写自己的逻辑,如果图像是一致的,我就认为内容没有变化
    @Override
    public boolean areContentsTheSame(int oldPosition, int newPosition) {
        return TextUtils.equals(mOldUserList.get(oldPosition).url, mNewUserList.get(newPosition).url);
    }

    //这个调用比较奇葩,要求也蛮多的,它需要areItemsTheSame()返回true,说明是同一条数据
    //但是又需要areContentsTheSame()返回false,告诉你虽然我们是同一条数据,但是我们也有不同的
    //它返回的是Object对象,我这里是返回的是Boolean对象,等会告诉你怎么用这个对象
    //当然了,你也可以返回任意的对象,到时候装换一下就可以了。
    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        User newUserEntity = mNewUserList.get(newItemPosition);
        return oldUserEntity.url.equals(newUserEntity.url);
    }
}

UserItemDiffCallBack完成之后,需要在RecyclerView.Adapter中调用含有三个参数的onBindViewHolder方法:

  // 这里的 payloads就是UserItemDiffCallBack中getChangePayload中返回的Object集合
  // 如果某个条目没有调用UserItemDiffCallBack#getChangePayload方法,那么那个条目对应的
  // onBindViewHolder中payloads就会为空数组对象

  //由于我返回的判断新旧数据的url是否相同,所以直接更新一个item的照片就可以了,对于Item其他的TextView对应的
  //name和age,数据没有变化,就没有必要更新了。
  //这里就是上面所说的,可以精确到某个View的更新了,比notifyItemChanged更加有效了。
  @Override
  public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
        if (payloads.isEmpty()) {
            super.onBindViewHolder(holder, position, payloads);
            return;
        }

        ImageLoader.load(holder.iv, mList.get(position).url);
    }

最后,有新旧数据更新时,在RecyclerView.Adapter中添加方法

    public void setData(List<User> userList) {
        // 获取DiffResut结果
        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new UserItemDiffCallBack(mList, userList));

        //使用DiffResult分发给apdate热更新
        diffResult.dispatchUpdatesTo(this);

        //按照DiffeResult定义好的逻辑,更新数据
        mList.clear();
        mList.addAll(userList);
    }

使用时:

 List<User> tempList = getNewUserList();
 mCommonAdapter2.setData(tempList);

此时更新数据时,将不再会有满屏白光闪烁,只更新数据变换的项,这个比notifyItemChanged(int position)帅气多了。

对于DiffUtil,基本流程图可以看一下下图:

本篇算是一个小小的开端,下一篇我打算总结一下DiffUtil的基本原理。

猜你喜欢

转载自blog.csdn.net/u013762572/article/details/82712205