RecyclerView各种更新功能总结

本文总结了RecyclerView的更新功能,重点介绍了DiffUtils的使用。DiffUtils能对比新旧数据并仅更新变动部分,提供更好的用户体验和性能。文章讨论了整体绑定和部分绑定两种方法,后者允许更高效的更新,仅刷新实际改变的控件内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

RecyclerView提升性能很重要的一点就是支持局部更新效果,以前的ListView如果修改了数据通常要调用notifyDatasetChanged导致整个ListView内的布局都要重新刷新,现在RecyclerView除了全部刷新的方法之外还提供了单项、多条数据更新的回调方法。

增删改交换

方法意义
notifyItemInserted(int item)通知增加了单条数据
notifyItemRemoved(int item)通知删除了单条数据
notifyItemChanged(int item)通知修改了单条数据
notifyItemMoved(int from, int to)通知移动了两条数据
notifyItemRangeInserted(int pos, int count)通知增加了一批数据
notifyItemRangeRemoved(int pos, int count)通知删除了一批数据
notifyDataSetChanged()通知所有的数据发生了变化,前面的更新方式都有动画效果(如果用户设置了),这种更新方式没有动画效果

DiffUtils整体绑定

DiffUtils是Android7.0新引入的工具,用来对比当前的旧数据和新得到的数据之间的差异,最后通过上面的各种通知操作完成更新,这种实现优点在数据基本上相同的情况下只更新修改的少量数据,不必对整体的列表数据做刷新操作,而且还支持数据的刷新的各种动画效果。实现时首先需要定义对比新旧数据的回调接口,然后工具计算会返回差异结果,最后将差异结果提交个Adapter来刷新RecyclerView,刷新的时候发现数据有变化的条目所有的控件都会被重新设置内容,所以也就是整体绑定,如果用户希望只设置内容改变的控件可以使用后面提到的部分绑定。

// RecyclerView.Adapter的子类方法,负责更新所有数据
public void replace() {
    this.data = userList;
    DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffUtil.Callback() {
        // 旧数据的数量
        @Override
        public int getOldListSize() {
            return users.size();
        }

        // 新数据数量
        @Override
        public int getNewListSize() {
            return userList.size();
        }

        // 数据条目是否是同一条数据,这里使用名字来判断是否是同一条数据
        @Override
        public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
            User user = users.get(oldItemPosition);
            User newUser = userList.get(newItemPosition);
            return user.getName().equalsIgnoreCase(newUser.getName());
        }

        // 同一条数据的内容是否发生过更改,这里对比User里的所有内容
        @Override
        public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
            User user = users.get(oldItemPosition);
            User newUser = userList.get(newItemPosition);
            return user.equals(newUser);
        }
    }, true);

    // 得到差异结果之后发布到Adapter并更新界面
    diffResult.dispatchUpdatesTo(this);
}

public void replaceAll() {
    this.data = userList;
    notifyDataSetChanged();
}

调用这种方式实现的批量更新效果如下:
这里写图片描述
同时在使用前面提到的notifyDatasetChanged方法来更新数据,再看下这种更新方式的实现效果:
这里写图片描述
对比发现DiffUtils的实现效果用户体验更好,而且性能更好,不过计算过程有时候非常的耗时,通常用户获取数据都是在子线程中,可以先在子线程中计算完结果之后再到主线程中更新界面。

DiffUtils部分绑定

上面的虽然已经做到不需要整体数据全部刷新,但是对于更改的数据条目整条都需要更新以便,加入条目里很多地方都没改,只改了一个文案,其实刷新其他控件就没有必要了,DiffUtils的回调接口里提供了一个getPayload的方法返回实际被更新的部分,然后在Adapter.onBindViewHolder(position, viewHolder, payload)里用这个方法里只刷新改变了的控件内容,实现更加高效的部分绑定。

@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
    User user = users.get(oldItemPosition);
    User newUser = userList.get(newItemPosition);

    // 将相同的数据条目里不同的部分记录下来
    Map<Integer, String> map = new HashMap<>();
    if (!user.getName().equalsIgnoreCase(newUser.getName())) {
        map.put(R.id.name_attr, newUser.getName());
    }

    if (!user.getDesc().equalsIgnoreCase(newUser.getDesc())) {
        map.put(R.id.desc_attr, newUser.getDesc());
    }

    if (user.getPortrait() != newUser.getPortrait()) {
        map.put(R.id.portrait_attr, String.valueOf(newUser.getPortrait()));
    }

    // 将记录不同数据的map返回作为payload对象
    return map;
}

覆盖Adapter的onBindViewHolder方法,记得需要是三个参数的那个方法,最后那个参数是一个List的参数,如果当时payload对应的View没有被attach到RecyclerView上,这个对象列表就是空的,当然如果默认用户没返回payload它一样是空的但不是null。在这里获取返回的payload对象并且遍历它内部记录的不同属性值,更新对应的控件内容。

@Override
public void onBindViewHolder(RecyclerViewHolder holder, int position, List<Object> payloads) {
    // 如果没有payload对象,那么直接采用整体绑定
    if (payloads.isEmpty()) {
        super.onBindViewHolder(holder, position, payloads);
    } else {
        // 如果有payload对象那么根据记录的更改内容更新对应的控件内容
        Map<Integer, String> map = (Map<Integer, String>) payloads.get(0);
        for (int strId : map.keySet()) {
            switch (strId) {
                case R.id.name_attr:
                    holder.name.setText(map.get(R.id.name_attr));
                    break;
                case R.id.desc_attr:
                    holder.desc.setText(map.get(R.id.desc_attr));
                    break;
                case R.id.portrait_attr:
                    holder.portrait.setImageResource(Integer.parseInt(map.get(R.id.portrait_attr)));
                    break;
            }
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值