美文网首页
Android——AsyncListDiffer

Android——AsyncListDiffer

作者: 四喜汤圆 | 来源:发表于2019-08-25 01:33 被阅读0次

AsyncListDiffer-RecyclerView最好的伙伴的学习笔记

一、作用

  1. DiffUtil.calculateDiff()方法运行在主线程中,当新旧 List 比较大时,该方法会阻塞主线程。
  2. 计算出 DiffUtil.Result 后,需要将新数据集先设置给 Adapter,然后调用 DiffUtil.diapatchUpdatesTo(adapter)刷新 UI,但很多人会忘记更新数据集。

DiffUtil已经挺好用了,但由于上述两个问题的存在,Google 工程师又推出了AsyncListDiff

二、概念

AsyncListDiff通过DiffUtil在后台线程计算两个 List 之间的差异。它和RecyceleView.Adapter相连接,可以将提交的 List 的变化通知给 Adapter。

它会在收到新的 List 后,开启后台线程使用DiffUtil计算新老数据集的差异。

1. 构造函数

public AsyncListDiffer(@NonNull RecyclerView.Adapter adapter,
    @NonNull DiffUtil.ItemCallback < T > diffCallback)

2. public 方法

(1)List<T> getCurrentList()

该方法可得到当前 List,通过该方法可安全地访问列表项和总大小。

如果 List 为null,或未提交 List,那么返回一个empty List这句话也说明,通过该方法返回的 List 不需要进行判 null,但需要判断是否是 empty

(2)void submitList(List<T> newList)

传递新的 List 到AdapterHelper

如果 List 已经存在并在展示,将开启线程,在后台计算新老数据集差异,计算完成后回调ListUpdateCallback,用新的 List 交换旧的。

三、使用

1. 创建 Adapter Item 对应的实体类

Android——DiffUtil

2. 改造型 Adapter

public class AsyncListDiffAdapter extends RecyclerView.Adapter < AsyncListDiffAdapter.MyViewHolder > {

    private AsyncListDiffer < User > mDiffer;

    public AsyncListDiffAdapter() {
        // 初始化AsyncListDiffer
        mDiffer = new AsyncListDiffer < User > (this, new AsyncListDiffCallback());
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_item, parent, false);
        return new MyViewHolder(view);
    }


    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.setData(mDiffer.getCurrentList().get(position));
    }

    @Override
    public int getItemCount() {
        // mDiffer.getCurrentList()得到的数据集不会为null,但可能未empty
        return mDiffer.getCurrentList().size();
    }

    // 向 Adapter 提交新的数据源
    public void submitList(List < User > data) {
        mDiffer.submitList(data);
    }

    // 得到数据源中的第position项
    public User getItem(int position) {
        return mDiffer.getCurrentList().get(position);
    }

    // 得到数据源
    public List < User > getData() {
        return mDiffer.getCurrentList();
    }

    class MyViewHolder extends RecyclerView.ViewHolder {
        private TextView nameTv;
        private TextView ageTv;
        private TextView profileTv;
        private TextView idTv;

        public MyViewHolder(View view) {
            super(view);
            idTv = view.findViewById(R.id.id_tv);
            nameTv = view.findViewById(R.id.name_tv);
            ageTv = view.findViewById(R.id.age_tv);
            profileTv = view.findViewById(R.id.profile_tv);
        }


        public void setData(User user) {
            // 绑定数据
        }
    }

}

有以下几个点需要注意

(1)自实现 DiffUtil.ItemCallback,给出 item 差异性计算条件

写法①
areContentsTheSame()中,默认 User 的内容是相同,逐个比较 User 的属性,只要有一项不同,则认为是不同的。

public class AsyncListDiffCallback extends DiffUtil.ItemCallback < User > {
    @Override
    public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) {
        // User的属性可能会变化,但ID is fixed(来自 Android官方文档)
        return oldItem.getId() == newItem.getId();
    }

    @Override
    public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) {
        // 默认内容是相同的,只要有一项不同,则返回false
        // name
        if (!oldItem.getName().equals(newItem.getName())) {
            return false;
        }
        // age
        if (oldItem.getAge() != newItem.getAge()) {
            return false;
        }
        // profile
        if (!oldItem.getProfile().equals(newItem.getProfile())) {
            return false;
        }
        return true;
    }
}

写法②
该写法参见 Android 官方文档。areContentsTheSame()中,如果使用了对象的equals()方法来比较二者是否相同,那 User 中必须重写euqals()方法。

public class AsyncListDiffCallback extends DiffUtil.ItemCallback < User > {
    @Override
    public boolean areItemsTheSame(@NonNull User oldItem, @NonNull User newItem) {
        // User的属性可能会变化,但ID is fixed(来自 Android官方文档)
        return oldItem.getId() == newItem.getId();
    }

    @Override
    public boolean areContentsTheSame(@NonNull User oldItem, @NonNull User newItem) {
        return oldItem.equals(newItem);
    }
}
@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;
    User user = (User) o;
    return age == user.age &&
        Objects.equals(name, user.name) &&
        Objects.equals(profile, user.profile);
}

@Override
public int hashCode() {
    return Objects.hash(name, age, profile);
}

(2)将所有对数据的操作代理给 AsyncListDiffer,这个 Adapter 是没有 List 数据的

@Override
public int getItemCount() {
    // mDiffer.getCurrentList()得到的数据集不会为null,但可能未empty
    return mDiffer.getCurrentList().size();
}

// 向 Adapter 提交新的数据源
public void submitList(List < User > data) {
    mDiffer.submitList(data);
}

// 得到数据源中的第position项
public User getItem(int position) {
    return mDiffer.getCurrentList().get(position);
}

// 得到数据源
public List < User > getData() {
    return mDiffer.getCurrentList();
}

(3)为 Adapter 创建构造函数,进行必要的初始化

  • Adapter 需要的上下文,可传入可不传入

写法①

private Context mContext;
private LayoutInflater mLayoutInflater;

public AsyncListDiffAdapter(Context context) {
    mContext = context;
    mLayoutInflater = LayoutInflater.from(context);
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = mLayoutInflater.inflate(R.layout.user_item, parent, false);
    return new MyViewHolder(view);
}

写法②

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.user_item, parent, false);
    return new MyViewHolder(view);
}
  • 初始化 AsyncListDiff 实例

该实例的创建需要传入 Adapter 和 DiffUtil.ItemCallback 的实例

3. 首先给 Adapter 设置数据,让它先跑起来

private void initViews() {
    mRecyclerView = findViewById(R.id.user_rv);
    mRefreshBtn = findViewById(R.id.btn_refresh);

    mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
    // 1,创建Adapter
    mAdapter = new AsyncListDiffAdapter();
    // 2,为RecyclerView设置适配器
    mRecyclerView.setAdapter(mAdapter);
    // 3,为Adapter提交数据源
    List < User > data = initData();
    mAdapter.submitList(data);
}
private List < User > initData() {
    List < User > data = new ArrayList < > ();
    data.add(new User(1, "福子", 10, "adfada"));
    data.add(new User(2, "大牛", 10, "adfada"));
    data.add(new User(1, "栓子", 10, "adfada"));
    data.add(new User(4, "铁柱", 10, "adfada"));
    data.add(new User(5, "钢蛋", 10, "adfada"));
    return data;
}

4. AsyncListDiff 实现定向刷新

private void refreshData() {
    // 新的数据源
    List < User > oldData = mAdapter.getData();
    List < User > newData = new ArrayList < > ();
    for (int i = 0; i < oldData.size(); i++) {
        newData.add(oldData.get(i).clone());
    }
    // 模拟新增数据
    newData.add(new User(6, "赵子龙", 100, "一个神人"));
    // 模拟数据修改
    newData.get(0).setName("福子222");
    newData.get(0).setProfile("这是一个有福的女子");
    // 模拟数据移位
    User user = newData.get(1);
    newData.remove(user);
    newData.add(user);

    // 将新数据集设置给Adapter
    mAdapter.submitList(newData);
}

四、评价

相比于普通的 Adapter,上述改造型 Adapter 中所有对数据的操作代理给 AsycListDiff,不是自己维护数据源的,调用submitList()更新数据源并刷新 UI。AsyncListDiffer 解决了 DiffUtil 的痛点,好用程度更上一层楼。

参考文献

Android Dev-AsyncListDiffer

相关文章

网友评论

      本文标题:Android——AsyncListDiffer

      本文链接:https://www.haomeiwen.com/subject/yyrvsctx.html