你可能不知道的在RecyclerView中实现局部刷新的技巧

3,059 阅读3分钟

写在前面

在上一年的时候我曾经写了一篇<<在RecyclerView中实现定点刷新>>的文章介绍了如何使用DataBinding框架进行局部刷新。时隔将近一年,期间我也换了一份工作,在新公司里并没有使用到DataBinding,所以如果想进行局部刷新ui,可能需要使用较早的adapter.notifyItemChanged(pos)

近期在做一个功能的时候,发现有一个比较简便可行的方法,可以达到局部刷新的效果。

那么方法是什么呢?

其实思考🤔一下,如果要更新一个TextView的文本,需要怎么做呢?

答案非常简单:那就是找到这个TextView,然后调用它的setText方法。

代码的话,可能是这样 :

findViewById<TexView>(R.id.titleTv).text = "title"

上面的是指普通的View,那么在RecyclerView呢?

其实答案差不多,我们只需要找到对应的ItemView或者它的ViewHolder,然后调用对应的渲染ui的方法就行了。先看看效果:

那应该怎么做呢?

一个简单的方法

其实RecyclerView已经提供了根据position获取ViewHolder这个功能。

从方法里可以看到RecyclerView会for循环去找holder,然后比较位置,如果不为null,且没有被移除,那么代表可以找到了,反之则为null。

那怎么找呢?

其中childCount是通过 this.mChildHelper.getUnfilteredChildCount()得到的。

其最终会调用RecyclerView的getChildCount()方法,返回的结果表示可见的item个数。比如当前可见的是8个,那么childCount即是8,所以你想获取超过屏幕最大Index的ViewHoler,那么就会返回null。比如当前最大可见的position为13,如果你想找第11个,能够获取到,但是找第15个,那么会返回null。

接着我们来拆解获取ViewHolder的方法,如下所示:

 getChildViewHolderInt(this.mChildHelper.getUnfilteredChildAt(i));

首先会通过getUnfilteredChildAt获取到index对应的view。这个方法最后也是调用的RecyclerView的getChildAt(offset)方法。

找到View之后,再用getChildViewHolderInt(view)获取对应的ViewHolder,其内部实现如下:

即通过LayoutParams获取到相应的ViewHolder。

而将ViewHolder赋值给LayoutParams的过程出现在RecyclerView的getViewForPosition(pos)方法中,最终会被LayoutManager调用。

回到主题,现在我们获取到了想要的ViewHoler,而且也知道了当前的Position,那么只需要再调用Adapter的

onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int)即可。

当然因为在Adapter中也能获取到RecyclerView,所以可以将更新的方法放入到Adapter中。

数据变化的时候,调用notifyItemUpdate(pos:Int)即可,可以说是很简单了。

写在后面

如果要说再优化的话,当然也有继续优化的点,比如减少无用的更新,或者说能在子线程通知更新UI,当然这些都是后话,以后再来完善。

GitHub示例:github.com/ditclear/Bi…

==================== 分割线 ======================

如果你想了解更多关于MVVM、Flutter、响应式编程方面的知识,欢迎关注我。

你可以在以下地方找到我:

简书:www.jianshu.com/u/117f1cf0c…

掘金:juejin.cn/user/817692…

Github: github.com/ditclear