recycleView局部更新item中的 局部控件 高效方法

最近 做项目,设计到一个recycleView里面 更新进度条的需求。在网上搜索了 一下,发现了一个方法。然后,自己又深入研究了一下 。现在写一下见解。
首先,我们来认识一下 RecyclerView.Adapter 里面的两个方法
第一个是: public void onBindViewHolder(@NonNull ViewHolder holder, int position)
第二个是:public void onBindViewHolder(@NonNull VH holder, int position, @NonNull List<Object> payloads) { onBindViewHolder(holder, position); }
第二个方法要和 notifyItemChanged(int position, @Nullable Object payload)这个方法一起用。这个一定要注意 和平常的notifyItemChanged 相比 多个 一个 payload 参数 。这个是我们自己自定义的值 用来标识 你想更新那个控件,对应于第二个里面的List payloads

如果你细心看的话,你会发现 第二个方法会多了一个参数 List< Object> payloads。这个参数是用来干什么用的呢? 看Android源码
在这里插这个入图片描述
这个意思呢 ,就是当payloads 不是空的时候, ViewHolder 只会进行 有效率的,局部的更新绑定。如果payloads为空的时候,那么Adapter 必须进行一个完整的绑定。

这样大家就明白了吧。在ViewHolder 调用周期里面,总是会先调用 带payloads参数的这个方法,再决定是否调用 不带payloads参数的方法。 在recycleView的绘制视图中,系统会默认的将payloads这个参数设置为空,所以我们如果不做设定的话,每次都是更新整个item
如果 要是你的item中含有 加载图片的话,你就会发现 每次刷新视图都会重新加载图片,浪费流量不说,也耗费性能。

接下来 我就写了一个小demo 用来验证一下
首先当然是我们的 item布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

   <Button
       android:id="@+id/one"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="one"
       />

    <Button
        android:id="@+id/two"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="two"
        />
    <Button
        android:id="@+id/three"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="three"
        />
</LinearLayout>

在这里插入图片描述

页面布局就是一个recycleView 这个就不贴代码了

为了贴近 真实项目 我还定义了bean 用于承载数据

package com.admin.plani.item;

/**
 * 创建时间 2018/9/26
 *
 * @author plani
 */
public class TestBean {
    private String image;
    private String size;
    private int age;

    public TestBean(String image, String size, int age) {
        this.image = image;
        this.size = size;
        this.age = age;
    }

    public String getImage() {
        return image;
    }

    public void setImage(String image) {
        this.image = image;
    }

    public String getSize() {
        return size;
    }

    public void setSize(String size) {
        this.size = size;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "TestBean{" +
                "image='" + image + '\'' +
                ", size='" + size + '\'' +
                ", age=" + age +
                '}';
    }
}

三个字段 image size age。

Adapter 里面的方法

首先是我们常用的这个 方法 大家一定很熟悉

 //这个方法 是全部刷新item 布局
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        TestBean bean = beans.get(position);
        System.out.println(bean);
        holder.one.setText(bean.getImage()+"");
        holder.two.setText(bean.getSize()+"");
        holder.three.setText(bean.getAge()+"");
    }

然后 是局部更新的这个方法

  /**
     * @param holder
     * @param position
     * @param payloads   用于标识 刷新布局里面的那个具体控件
     */
    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
        System.out.println(">>>>payloads"+payloads);
        if (payloads.isEmpty()){
            super.onBindViewHolder(holder, position, payloads);
            return;
        }
        TestBean bean = beans.get(position);
        //循环得到payloads里面的参数 
        for (Object payload:payloads) {
            switch (String.valueOf(payload)){
                case "one":
                    holder.one.setText(bean.getImage()+"");
                    break;
                case "two":
                    holder.two.setText(bean.getSize()+"");
                    break;
                case "three":
                    holder.three.setText(bean.getAge()+"");
                    break;
                default:
                    break;
            }
        }
    }

在上面的这个方法里面 ,我们首先判断 payloads是否为空,为空就是全部刷新 直接调用 父类方法,父类方法 就是上面的onBindViewHolder(@NonNull ViewHolder holder, int position)

如果 不为空 就取出里面的值,根据值 进行更新你想要更新的局部控件。大家可能疑问 这里面的从哪里传来的?
就是上文我们提到的 要一起用的方法里面参数。细心的人还会发现 notifyItemChanged(int position, @Nullable Object payload)里面的payload是Object类型 ,不是List啊,这个就是我接下来要说的。 如果我们在要更新局部控件的地方 多次同时调用 notifyItemChanged(int position, payload) 。那么系统只会调用一次onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List payloads) 这个局部刷新 方法 。然后 会把你多次传入的payload 合并成 list集合

就像下面这样

 testAdapter = new TestAdapter((view, position) -> {
              beans.get(position).setAge(18);
             testAdapter.notifyItemChanged(position,"one");
             testAdapter.notifyItemChanged(position,"two");
             testAdapter.notifyItemChanged(position,"three");
        }, beans);

在这里插入图片描述

这是 我们一开始时候log

在外面局部刷新item控件的代码后
在这里插入图片描述
看这里 我们在外面调用了三次notifyItemChanged 传入了不同的值 ,系统把三个合并了变成一个list,在依次进行刷新。

最后 强调一下如果想局部刷新item的控件 ,**一定要调用notifyItemChanged(int position, @Nullable Object payload)**这个方法 。
注意 是两个参数。

最后附上我的demo recycleView item局部控件刷新

如果有不懂的,可以关注我的公众号 “知我饭否” 向我留言。我也会每天更新一些文章,有兴趣的可以扫描下方的二维码

在这里插入图片描述

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 像素格子 设计师:CSDN官方博客 返回首页