最近 做项目,设计到一个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局部控件刷新