添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

notifyDataSetChanged

notifyItemChanged(int position)

notifyItemChanged(int position, @Nullable Object payload)

一般来说一个 item 是由多个控件组成的,如 Button、CheckBox、TextView、ImageView、ViewGroup 等组合。当我们点击item的某个控件时,RV 需要重新计算布局、刷新视图来响应交互。假设一个 item 包含了N多个控件,如果调用 notifyItemChanged(int position) 时,item 中的每个控件都需要重新布局显示,无形中加大了内存和性能的损耗。

就算我们不考虑内存和性能的问题,那么一些效果也是我们无法接受的,当 item 刷新的时候会导致内部的图片或item 出现一次闪烁。

以我的精神粮食起点为例,如下

所以我们才需要用到类似 Payload 与 Diff 之类的刷新方式。

下面我们就一起看看它们是怎么使用的。

Payload的刷新

我们通过 notifyItemChanged(int position, @Nullable Object payload) 来刷新指定索引的item。

在 RV 的 Adapter 中的 onBindViewHolder 可以接收到 payloads 参数,这个 payloads 参数是一个 List 对象,该对象不是 null 但可能是空的。

public void onBindViewHolder (VH holder, int position, List<Object> payloads) {

onBindViewHolder(holder, position);

通过 Adapter 的 notifyXXX 函数的带有 payload 参数的函数可以设置 payload 对象,如:

public final void notifyItemChanged ( int position, Object payload) {

mObservable.notifyItemRangeChanged(position, 1 , payload);

由于 onBindViewHolder 有重载的方法,如果使用了 payloads 的方式,那么我们需要做兼容,如果没有 payloads 就去走整个 item 的刷新,如果有 payloads 那么我们就根据指定的 payload 去刷新指定的数据。

@Override

public void onBindViewHolder(ViewHolder holder, int position, List < Object > payloads) {

if (!payloads.isEmpty && payloads. get ( 0 ).equals( "like" )) {

//如果是我们指定的 payloads ,那么就可以指定刷新

TipsBean item = mData. get (position);

holder.tvLikeNum.setText(R.id.tv_tips_like_num, item.likes_count > 0 ? item.likes_count + "" : "Like" );

holder.tvLikeNum.setTextColor(R.id.tv_tips_like_num, item.likes_count > 0 ? CommUtils.getColor(R.color.black) : CommUtils.getColor(R.color.home_item_text_light_gray));

} else {

// 如果没有 payloads ,或者不是我们指定的,还是返回默认的整个刷新

super .onBindViewHolder(holder, position);

如果没有 payload ,当调用 notifyItemChanged 时,RV 会通过回调 onBindViewHolder(holder, position) 来更新当前数据变化的 item ,此时会触发 整个 item 中 view 的重新布局和计算位置,这样的话只要是其中一个 View 状态变化了,最终会导致整个 item 都需要重新布局一遍。

例如上述的例子,在评论的列表中,我只点赞了第一个 item ,我们就通过 payload 来告诉 RV 这个 item 中的 like 文本变化了,那么我们就只需要处理 Like 文本的变化。

Diff的刷新与快速实现方法

每一个 payloads 都要写一次,然后我们在调用 notifyItemChanged 时万一写错了怎么办?有没有一种方式能让程序自动管理,让程序帮我们记录 payloads ?最好还能帮助我们自动排序!

有,我们先看看自动排序的方式: SortedList 的方式。

SortedList,顾名思义就是排序列表,它适用于列表有序且不重复的场景。并且SortedList会帮助你比较数据的差异,定向刷新数据。而不是简单粗暴的 notifyDataSetChanged

例如我们定义一个城市排序的对象:

public class City {

private int id;

private String cityName;

private String firstLetter;

我们需要进行排序的规则定义。

public class SortedListCallback extends SortedListAdapterCallback < City > {

public SortedListCallback (RecyclerView.Adapter adapter) {

super (adapter);

* 排序条件

@Override

public int compare (City o1, City o2) {

return o1.getFirstLetter.compareTo(o2.getFirstLetter);

* 用来判断两个对象是否是相同的Item。

@Override

public boolean areItemsTheSame (City item1, City item2) {

return item1.getId == item2.getId;

* 用来判断两个对象是否是内容的Item。

@Override

public boolean areContentsTheSame (City oldItem, City newItem) {

if (oldItem.getId != newItem.getId) {

return false ;

return oldItem.getCityName.equals(newItem.getCityName);

再然后在Adapte中使用的时候,不需要Arrylist,要用排序的集合,新的对象SortedList排序集合。

public class SortedAdapter extends RecyclerView . Adapter < SortedAdapter . ViewHolder > {

// 数据源使用SortedList

private SortedList<City> mSortedList;

private LayoutInflater mInflater;

public SortedAdapter (Context mContext) {

mInflater = LayoutInflater.from(mContext);

public void setSortedList (SortedList<City> mSortedList) {

this .mSortedList = mSortedList;

* 批量更新操作,例如:

* <pre>

* mSortedList.beginBatchedUpdates;

* try {

* mSortedList.add(item1)

* mSortedList.add(item2)

* mSortedList.remove(item3)

* ...

* } finally {

* mSortedList.endBatchedUpdates;

* </pre>

public void addData (List<City> mData) {

mSortedList.beginBatchedUpdates;

mSortedList.addAll(mData);

mSortedList.endBatchedUpdates;

* 移除item

public void removeData ( int index) {

mSortedList.removeItemAt(index);

* 清除集合

public void clear {

mSortedList.clear;

@Override

@NonNull

public SortedAdapter. ViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {

return new ViewHolder(mInflater.inflate(R.layout.item_test, parent, false ));

@Override

public void onBindViewHolder (@NonNull SortedAdapter.ViewHolder holder, final int position) {

// 。。。

@Override

public int getItemCount {

return mSortedList.size;

public class ViewHolder extends RecyclerView . ViewHolder {

public ViewHolder (View itemView) {

super (itemView);

使用的时候:

RecyclerView mRecyclerView = findViewById(R.id.rv);

mRecyclerView.setLayoutManager( new LinearLayoutManager( this ));

mSortedAdapter = new SortedAdapter( this );

// SortedList初始化

SortedListCallback mSortedListCallback = new SortedListCallback(mSortedAdapter);

SortedList mSortedList = new SortedList<>(City. class , mSortedListCallback);

mSortedAdapter.setSortedList(mSortedList);

mRecyclerView.setAdapter(mSortedAdapter);

//添加数据

这样确实就能实现自动排序,刷新列表了,相对 notifyDataSetChanged 的暴力刷新,优雅一点。但是它没有 payload 的功能,这个刷新只是刷新的是整个 RV 中的部分Item,但还是刷新整个 item 啊。

有没有办法既能排序又能 payload差异化刷新的方式呢?

肯定有哇, DiffUtil 的方式就此诞生,常用的相关的几个类为 DiffUtil AsyncListDiffer ListAdapter(用于快速实现的封装类)

DiffUtil 能实现排序加payload局部刷新的功能:

  • 当某个 item 的位置变化,触发排序逻辑,有移除和添加的动画。

  • 当某个 item 的位置不变,内容变化,触发 payload 局部刷新。

  • 在子线程中计算DiffResult,在主线程中刷新RecyclerView。

  • AsyncListDiffer 又是什么东西,为什么需要它。

    其实 AsyncListDiffer 就是集成了 AsyncListUtil + DiffUtil 的功能,由于 DiffUtil在计算数据差异 DiffUtil.calculateDiff(mDiffCallback) 是一个耗时操作,需要我们放到子线程去处理,最后在主线程刷新,为了我们开发者更加的方便,谷歌直接提供了 AsyncListDiffer 方便我们直接使用。看来谷歌是怕我们开发者不会使用子线程,直接给我们写好了。

    ListAdapter 又是个什么鬼?怎么越来越复杂了?

    其实谷歌就喜欢把简单的东西复杂化,如果我们使用 AsyncListDiffer 去实现的话,虽然不用我们操心子线程了,但是还是需要我们定义对象、集合、添加数据的方法 ,如 addNewData ,里面调用 mDiffer.submitList 才能实现。

    谷歌还是怕我们不会用吧!直接把 AsyncListDiffer 的使用都给简化了,直接提供了 ListAdapter 包装类,内部对 AsyncListDiffer 的使用做了一系列的封装,使用的时候我们的 RV-Adapter 直接继承 ListAdapter 即可实现 AsyncListDiffer 的功能,内部连设置数据的方法都给我们提供好了。谷歌真的是我们的好爸爸!为我们开发者操碎了心。

    那它们都到底 怎么使用 呢?

    不管什么方式,我们都需要定义好自己的 DiffUtil.CallBack ,毕竟就算让程序帮我们排序和差分,我们也得告诉程序排序的规则和diff的规则,是吧!

    public class MyDiffUtilItemCallback extends DiffUtil . ItemCallback < TestBean > {

    * 是否是同一个对象

    @Override

    public boolean areItemsTheSame( @NonNull TestBean oldItem, @NonNull TestBean newItem) {

    return oldItem.getId == newItem.getId;

    * 是否是相同内容

    @Override

    public boolean areContentsTheSame( @NonNull TestBean oldItem, @NonNull TestBean newItem) {

    return oldItem.getName.equals(newItem.getName);

    * areItemsTheSame返回true而areContentsTheSame返回false时调用,也就是说两个对象代表的数据是一条,

    * 但是内容更新了。此方法为定向刷新使用,可选。

    @Nullable

    @Override

    public Object getChangePayload( @NonNull TestBean oldItem, @NonNull TestBean newItem) {

    Bundle payload = new Bundle;

    if (!oldItem.getName.equals(newItem.getName)) {

    payload.putString( "KEY_NAME" , newItem.getName);

    if (payload.size == 0 ){

    //如果没有变化 就传空

    return null ;

    return payload;

    那个Diff的具体实现我们选用哪一种方案呢?其实三种方式都是可以实现的,这里我们先使用 AsyncListDiffer 的方式来实现。

    上面关于 AsyncListDiffer 的介绍我们说过了,虽然不需要我们实现异步操作了,但是我们还是需要实现对象、集合、添加数据的方法等。

    示例如下:

    public class AsyncListDifferAdapter extends RecyclerView . Adapter < AsyncListDifferAdapter . ViewHolder > {

    private LayoutInflater mInflater;

    // 数据的操作由AsyncListDiffer实现

    private AsyncListDiffer<TestBean> mDiffer;

    public AsyncListDifferAdapter (Context mContext) {

    // 初始化AsyncListDiffe

    mDiffer = new AsyncListDiffer<>( this , new MyDiffUtilItemCallback);

    mInflater = LayoutInflater.from(mContext);

    //添加数据传对象和对象集合都可以

    public void addData (TestBean mData) {

    List<TestBean> mList = new ArrayList<>;

    mList.addAll(mDiffer.getCurrentList);

    mList.add(mData);

    mDiffer.submitList(mList);

    public void addData (List<TestBean> mData) {

    // 由于DiffUtil是对比新旧数据,所以需要创建新的集合来存放新数据。

    // 实际情况下,每次都是重新获取的新数据,所以无需这步。

    List<TestBean> mList = new ArrayList<>;

    mList.addAll(mData);

    mDiffer.submitList(mList);

    //删除数据,要先获取全部集合,再删除指定的集合,再提交删除之后的集合

    public void removeData ( int index) {

    List<TestBean> mList = new ArrayList<>;

    mList.addAll(mDiffer.getCurrentList);

    mList.remove(index);

    mDiffer.submitList(mList);

    public void clear {

    mDiffer.submitList( null );

    @Override

    @NonNull

    public AsyncListDifferAdapter. ViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {

    return new ViewHolder(mInflater.inflate(R.layout.item_test, parent, false ));

    @Override

    public void onBindViewHolder (@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {

    if (payloads.isEmpty) {

    onBindViewHolder(holder, position);

    } else {

    Bundle bundle = (Bundle) payloads.get( 0 );

    holder.mTvName.setText(bundle.getString( "KEY_NAME" ));

    @Override

    public void onBindViewHolder (@NonNull AsyncListDifferAdapter.ViewHolder holder, final int position) {

    TestBean bean = mDiffer.getCurrentList.get(position);

    holder.mTvName.setText(bean.getName);

    @Override

    public int getItemCount {

    return mDiffer.getCurrentList.size;

    static class ViewHolder extends RecyclerView . ViewHolder {

    ......

    由于 Diff.Callback 我们已经在 Adapter 内部已经初始化了,所以使用的时候我们直接像普通的 RV 设置 Adapter 一样即可。

    在更新数据的时候我们使用 Adapter 定义的 addData removeData 即可完成Diff刷新。

    使用 ListAdapter 会怎样?

    如果觉得使用 AsyncListDiffer 都嫌弃麻烦的话,我们直接使用 ListAdapter 也能实现。

    由于我们还是需要一个List集合去保存我们的数据,我们就能对 ListAdapter 再做一个简单的基类封装。

    public abstract class BaseRVDifferAdapter < T , VH extends RecyclerView . ViewHolder > extends ListAdapter < T , VH > {

    protected Context mContext;

    protected LayoutInflater mInflater;

    protected List<T> mDatas = new ArrayList<>;

    public BaseRVDifferAdapter (Context context, DiffUtil.ItemCallback<T> callback) {

    super (callback);

    mContext = context;

    mInflater = LayoutInflater.from(mContext);

    //设置数据源

    protected void setData (List<T> list) {

    mDatas.clear;

    mDatas.addAll(list);

    List<T> mList = new ArrayList<>;

    mList.addAll(mDatas);

    submitList(mList);

    //添加数据源

    protected void addData (List<T> list) {

    mDatas.addAll(list);

    List<T> mList = new ArrayList<>;

    mList.addAll(mDatas);

    submitList(mList);

    //删除制定索引数据源

    protected void removeData ( int index) {

    mDatas.remove(index);

    List<T> mList = new ArrayList<>;

    mList.addAll(mDatas);

    submitList(mList);

    //清除全部数据

    protected void clear {

    mDatas.clear;

    submitList( null );

    //获取adapter维护的数据集合

    protected List<T> getAdapterData {

    return mDatas;

    我们实现Adater的方法就如下:

    public class MyListAdapter extends BaseRVDifferAdapter < TestBean , MyListAdapter . ViewHolder > {

    public MyListAdapter (Context context) {

    super (context, new MyDiffUtilItemCallback);

    @Override

    @NonNull

    public MyListAdapter. ViewHolder onCreateViewHolder (@NonNull ViewGroup parent, int viewType) {

    return new ViewHolder(mInflater.inflate(R.layout.item_test, parent, false ));

    @Override

    public void onBindViewHolder (@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {

    if (payloads.isEmpty) {

    onBindViewHolder(holder, position);

    } else {

    Bundle bundle = (Bundle) payloads.get( 0 );

    holder.mTvName.setText(bundle.getString( "KEY_NAME" ));

    @Override

    public void onBindViewHolder (@NonNull MyListAdapter.ViewHolder holder, final int position) {

    TestBean bean = getItem(position);

    holder.mTvName.setText(bean.getName);

    static class ViewHolder extends RecyclerView . ViewHolder {

    TextView mTvName;

    ViewHolder(View itemView) {

    super (itemView);

    mTvName = itemView.findViewById(R.id.tv_name);

    在我们自己的 Adpater 中,我们还是需要初始化我们自己的 Diff.Callback 的。那么在使用的时候也就和普通的RV设置 Adapter 是一样的。

    @ Override

    protected void onCreate ( Bundle savedInstanceState ) {

    super.onCreate(savedInstanceState);

    setContentView(R.layout.activity_sorted_list);

    RecyclerView mRecyclerView = findViewById(R.id.rv);

    mRecyclerView.setLayoutManager( new LinearLayoutManager( this ));

    mAsyncListDifferAdapter = new AsyncListDifferAdapter( this );

    mRecyclerView.setAdapter(mAsyncListDifferAdapter);

    initData;

    private void addData {

    List<TestBean> mList = new ArrayList;

    for ( int i = 10 ; i < 20 ; i++){

    mList. add ( new TestBean(i, "Item " + i));

    mMyListAdapter.addData(mList);

    private void initData {

    List<TestBean> mList = new ArrayList;

    for ( int i = 0 ; i < 10 ; i++){

    mList. add ( new TestBean(i, "Item " + i));

    mMyListAdapter.setData(mList);

    private void updateData {

    List<TestBean> mList = new ArrayList;

    for ( int i = 20 ; i < 30 ; i++){

    mList. add ( new TestBean(i, "Item " + i));

    mMyListAdapter.addData(mList);

    是不是可以无脑使用Diff的方式呢?也不是,当一些普通长列表我们使用默认的RV-Adapter就行了,加载更多,往源数据上面加数据,并不涉及到很多数据源改变的也没必要使用Diff。

    那么哪里使用?比如IM的会话列表,长期变动的数据,比如评论区域热评区的数据切换时,比如我们的Android掘金App:

    直接暴力的切换数据源然后 notifyDataSetChanged ,这样就会整个页面闪烁。而对于这些特定的一些场景,我们使用 Diff 的功能,就会感觉更加的流畅,体验会好一点哦!

    DiffUtil的封装

    既然 AsyncListDiffer ListAdapter 都是快速实现的方式,那我们直接使用基本 DiffUtil 行不行?

    当然可以,我不想直接使用 ListAdapter ,我就想用 RV.Adapter ,因为我有其他的封装与扩展,我自己会使用异步线程,我不需要你帮我管理,我不需要使用你的 AsyncListDiffer 帮我管理我的 List 对象 。

    我们可以直接使用基本的 DiffUtil 。真实使用下来也不是很复杂,只需要做一点点的封装也能很方便的实现逻辑。

    先写一个Differ的配置文件,内部可配置主线程,异步线程,和必备的 DiffUtil.Callback

    class MyAsyncDifferConfig < T > (

    @SuppressLint( "SupportAnnotationUsage" )

    @RestrictTo(RestrictTo.Scope.LIBRARY)

    val mainThreadExecutor: Executor?,

    val backgroundThreadExecutor: Executor,

    val diffCallback: DiffUtil.ItemCallback<T>) {

    class Builder < T > ( private val mDiffCallback: DiffUtil.ItemCallback<T>) {

    companion object {

    private val sExecutorLock = Any

    private var sDiffExecutor: Executor? = null

    private var mMainThreadExecutor: Executor? = null

    private var mBackgroundThreadExecutor: Executor? = null

    fun setMainThreadExecutor (executor: Executor ?) : Builder<T> {

    mMainThreadExecutor = executor

    return this

    fun setBackgroundThreadExecutor (executor: Executor ?) : Builder<T> {

    mBackgroundThreadExecutor = executor

    return this

    fun build : MyAsyncDifferConfig<T> {

    if (mBackgroundThreadExecutor == null ) {

    synchronized(sExecutorLock) {

    if (sDiffExecutor == null ) {

    sDiffExecutor = Executors.newSingleThreadExecutor

    mBackgroundThreadExecutor = sDiffExecutor

    return MyAsyncDifferConfig(

    mMainThreadExecutor,

    mBackgroundThreadExecutor!!,

    mDiffCallback)

    重点是定义一个自己的 AsyncDiffer ,内部使用 DiffUtil 来计算差分。

    class MyAsyncDiffer < T > (

    private val adapter: RecyclerView.Adapter<*>,

    private val config: MyAsyncDifferConfig<T>

    private val mUpdateCallback: ListUpdateCallback = AdapterListUpdateCallback(adapter)

    private var mMainThreadExecutor: Executor = config.mainThreadExecutor ?: MainThreadExecutor

    private val mListeners: MutableList<ListChangeListener<T>> = CopyOnWriteArrayList

    private var mMaxScheduledGeneration = 0

    private var mList: List<T>? = null

    private var mReadOnlyList = emptyList<T>

    private class MainThreadExecutor internal constructor : Executor {

    val mHandler = Handler(Looper.getMainLooper)

    override fun execute (command: Runnable ) {

    mHandler.post(command)

    fun getCurrentList : List<T> {

    return mReadOnlyList

    @JvmOverloads

    fun submitList (newList: MutableList < T >?, commitCallback: Runnable ? = null ) {

    val runGeneration: Int = ++mMaxScheduledGeneration

    if (newList == mList) {

    commitCallback?.run

    return

    val previousList = mReadOnlyList

    if (newList == null ) {

    val countRemoved = mList?.size ?: 0

    mList = null

    mReadOnlyList = emptyList

    mUpdateCallback.onRemoved( 0 , countRemoved)

    onCurrentListChanged(previousList, commitCallback)

    return

    if (mList == null ) {

    mList = newList

    mReadOnlyList = Collections.unmodifiableList(newList)

    mUpdateCallback.onInserted( 0 , newList.size)

    onCurrentListChanged(previousList, commitCallback)

    return

    val oldList: List<T> = mList as List<T>

    config.backgroundThreadExecutor.execute {

    val result = DiffUtil.calculateDiff( object : DiffUtil.Callback {

    override fun getOldListSize : Int {

    return oldList.size

    override fun getNewListSize : Int {

    return newList.size

    override fun areItemsTheSame (oldItemPosition: Int , newItemPosition: Int ) : Boolean {

    val oldItem: T? = oldList[oldItemPosition]

    val newItem: T? = newList[newItemPosition]

    return if (oldItem != null && newItem != null ) {

    config.diffCallback.areItemsTheSame(oldItem, newItem)

    } else oldItem == null && newItem == null

    override fun areContentsTheSame (oldItemPosition: Int , newItemPosition: Int ) : Boolean {

    val oldItem: T? = oldList[oldItemPosition]

    val newItem: T? = newList[newItemPosition]

    if (oldItem != null && newItem != null ) {

    return config.diffCallback.areContentsTheSame(oldItem, newItem)

    if (oldItem == null && newItem == null ) {

    return true

    throw Asserti

    override fun getChangePayload (oldItemPosition: Int , newItemPosition: Int ) : Any? {

    val oldItem: T? = oldList[oldItemPosition]

    val newItem: T? = newList[newItemPosition]

    if (oldItem != null && newItem != null ) {

    return config.diffCallback.getChangePayload(oldItem, newItem)

    throw Asserti

    mMainThreadExecutor.execute {

    if (mMaxScheduledGeneration == runGeneration) {

    latchList(newList, result, commitCallback)

    private fun latchList (

    newList: MutableList < T >,

    diffResult: DiffUtil . DiffResult ,

    commitCallback: Runnable ?

    val previousList = mReadOnlyList

    mList = newList

    mReadOnlyList = Collections.unmodifiableList(newList)

    diffResult.dispatchUpdatesTo(mUpdateCallback)

    onCurrentListChanged(previousList, commitCallback)

    private fun onCurrentListChanged (

    previousList: List < T >,

    commitCallback: Runnable ?

    for (listener in mListeners) {

    listener.onCurrentListChanged(previousList, mReadOnlyList)

    commitCallback?.run

    //定义接口

    interface ListChangeListener < T > {

    fun onCurrentListChanged (previousList: List < T >, currentList: List < T >)

    fun addListListener (listener: ListChangeListener < T >) {

    mListeners.add(listener)

    fun removeListListener (listener: ListChangeListener < T >) {

    mListeners.remove(listener)

    fun clearAllListListener {

    mListeners.clear

    然后我们可以用委托的方式配置,可以让普通的 RecyclerView.Adapter 也能通过配置的方式选择是否使用Differ。

    实现我们的控制类接口。

    //设置别名简化

    typealias IDiffer<T> = IMyDifferController<T>

    fun <T> differ : MyDifferController<T> = MyDifferController

    interface IMyDifferController < T > {

    fun RecyclerView.Adapter <*> . initDiffer (config: MyAsyncDifferConfig < T >) : MyAsyncDiffer<T>

    fun getDiffer : MyAsyncDiffer<T>?

    fun getCurrentList : List<T>

    fun setDiffNewData (list: MutableList < T >, commitCallback: Runnable ? = null )

    fun addDiffNewData (list: MutableList < T >, commitCallback: Runnable ? = null )

    fun addDiffNewData (t: T , commitCallback: Runnable ? = null )

    fun removeDiffData (index: Int )

    fun clearDiffData

    fun RecyclerView.Adapter <*> . onCurrentListChanged (previousList: List < T >, currentList: List < T >)

    在对控制类接口实例化,做一些具体的操作逻辑。

    class MyDifferController < T > : IMyDifferController < T > {

    private var mDiffer: MyAsyncDiffer<T>? = null

    override fun RecyclerView.Adapter <*> . initDiffer (config: MyAsyncDifferConfig < T >) : MyAsyncDiffer<T> {

    mDiffer = MyAsyncDiffer( this , config)

    val mListener: MyAsyncDiffer.ListChangeListener<T> = object : MyAsyncDiffer.ListChangeListener<T> {

    override fun onCurrentListChanged (previousList: List < T >, currentList: List < T >) {

    this @initDiffer .onCurrentListChanged(previousList, currentList)

    mDiffer?.addListListener(mListener)

    return mDiffer!!

    override fun getDiffer : MyAsyncDiffer<T>? {

    return mDiffer

    override fun getCurrentList : List<T> {

    return mDiffer?.getCurrentList ?: emptyList

    override fun setDiffNewData (list: MutableList < T >, commitCallback: Runnable ?) {

    mDiffer?.submitList(list, commitCallback)

    override fun addDiffNewData (list: MutableList < T >, commitCallback: Runnable ?) {

    val newList = mutableListOf<T>

    newList.addAll(mDiffer?.getCurrentList ?: emptyList)

    newList.addAll(list)

    mDiffer?.submitList(newList, commitCallback)

    override fun addDiffNewData (t: T , commitCallback: Runnable ?) {

    val newList = mutableListOf<T>

    newList.addAll(mDiffer?.getCurrentList ?: emptyList)

    newList.add(t)

    mDiffer?.submitList(newList, commitCallback)

    override fun removeDiffData (index: Int ) {

    val newList = mutableListOf<T>

    newList.addAll(mDiffer?.getCurrentList ?: emptyList)

    newList.removeAt(index)

    mDiffer?.submitList(newList)

    override fun clearDiffData {

    mDiffer?.submitList( null )

    override fun RecyclerView.Adapter <*> . onCurrentListChanged (previousList: List < T >, currentList: List < T >) {

    到此我们就能封装一个DiffUtil的工具类了,我们可以选择是否启用Diff,例如我们不使用Diff,我们使用Adapter就是一个普通的Adaper。

    class MyDiffAdapter : RecyclerView.Adapter<BaseViewHolder> {

    private val mDatas = arrayListOf<DemoDiffBean>

    fun addData (list : List < DemoDiffBean >) {

    mDatas.addAll(list)

    notifyDataSetChanged

    override fun onBindViewHolder (holder: BaseViewHolder , position: Int , payloads: MutableList < Any >) {

    if (!CheckUtil.isEmpty(payloads) && (payloads[ 0 ] as String) == "text" ) {

    YYLogUtils.w( "差分刷新 -------- 文本更新" )

    holder.setText(R.id.tv_job_text, mDatas[position].content)

    } else {

    onBindViewHolder(holder, position)

    override fun onBindViewHolder (holder: BaseViewHolder , position: Int ) {

    YYLogUtils.w( "默认数据赋值 --------" )

    holder.setText(R.id.tv_job_text, mDatas[position].content)

    override fun onCreateViewHolder (parent: ViewGroup , viewType: Int ) : BaseViewHolder {

    return BaseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_diff_jobs, parent, false ))

    override fun getItemCount : Int {

    return mDatas.size

    如果我们想启动Diff的功能的时候,实现这个接口并委托实现即可启用Diff。

    class MyDiffAdapter : RecyclerView.Adapter<BaseViewHolder>, IDiffer<DemoDiffBean> by differ {

    init {

    initDiffer(MyAsyncDifferConfig.Builder(DiffDemoCallback).build)

    override fun onBindViewHolder (holder: BaseViewHolder , position: Int , payloads: MutableList < Any >) {

    if (!CheckUtil.isEmpty(payloads) && (payloads[ 0 ] as String) == "text" ) {

    YYLogUtils.w( "差分刷新 -------- 文本更新" )

    holder.setText(R.id.tv_job_text, getCurrentList[position].content)

    } else {

    onBindViewHolder(holder, position)

    override fun onBindViewHolder (holder: BaseViewHolder , position: Int ) {

    YYLogUtils.w( "默认数据赋值 --------" )

    holder.setText(R.id.tv_job_text, getCurrentList[position].content)

    override fun onCreateViewHolder (parent: ViewGroup , viewType: Int ) : BaseViewHolder {

    return BaseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_diff_jobs, parent, false ))

    override fun getItemCount : Int {

    return getCurrentList.size

    使用的时候也是超方便的。

    private fun initRV {

    mAdapter = MyDiffAdapter

    findViewById<RecyclerView>(R.id.recyclerView).vertical.apply {

    adapter = mAdapter

    divider(Color.BLACK)

    private fun initData {

    mDatas.clear

    for (i in 1..10 ) {

    mDatas.add(DemoDiffBean(i, "conetnt: $i " ))

    mAdapter.setDiffNewData(mDatas)

    private fun initListener {

    findViewById<View>(R.id.diff_1).click {

    val list = mutableListOf<DemoDiffBean>

    for (i in 1..10 ) {

    list.add(DemoDiffBean(i, "Diff1 conetnt: $i " ))

    mAdapter.setDiffNewData(list)

    findViewById<View>(R.id.diff_2).click {

    val list = mutableListOf<DemoDiffBean>

    for (i in 1..10 ) {

    list.add(DemoDiffBean(i, "Diff3 conetnt: $i " ))

    list.removeAt( 0 )

    list.removeAt( 1 )

    list.removeAt( 2 )

    list[ 3 ].content = "自定义乱改的数据"

    mAdapter.setDiffNewData(list)

    运行的效果如下:

    是不是很方便呢?可能有人会问,这么封装有什么好处?

    其实这样通过配置的方式,我们可以使用在任意的 RV.Adapter 上面,包括我们自己定义的 BaseAdapter LoadMoreAdapter 等。相对比较灵活吧,方便在原有的效果上快速修改。

    当然了,其实关于 Diff 的实现,有这么多种方式可以让大家使用,每一种方案都能实现同样的效果,只是看大家愿不愿意优化而已,每一种方式使用起来都不算难。

    关于 paylpoad 和 Diff 的问题,既然 Diff 是在 payload 的基础上实现的,那是不是有 Diff 功能之后我们就不需要手动 payload 了呢?

    也不是,上面的介绍中已经讲过了,如果数据频繁的切换,最好是使用Diff,如果就是类似普通的评论列表点赞的效果,我们手动 payload 即可。他们有各自的使用场景。

    需要注意的是,如果使用 Diff 要留意对象指针的问题,DiffUtil 首先检查新提交的 newList 与内部持有的 mList 的引用是否相同, 如果相同, 就直接返回。如果不同的引用,才会对 newList 和 mList 做 Diff 算法比较。

    可以看的我的 Demo 都是直接另 new 一个 List 来进行操作的,正常开发场景一般我们都是从服务器拿的不同的 List,也不会有问题。而如果从本地拿的数据去比对的时候就需要注意,对比的对象和原有的对象是否是同一个对象,此时可以考虑对象的深拷贝来实现新对象,再拿去和原有对象进行差分对比,关于浅拷贝与深拷贝的对比和如何深拷贝,可以看我之前的 文章

    好了,本文的全部代码与Demo都已经开源。有兴趣可以看 这里 。项目会持续更新,大家可以关注一下。

    https://gitee.com/newki123456/Kotlin-Room

    如果感觉本文对你有一点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。

    Ok,这一期就此完结。

    最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!

    Android 干货分享:插件化换肤原理

    framework该这么学,原来系统服务这样设计的?

    全网超优雅Android控件可见性检测

    扫一扫 关注我的公众号

    如果你想要跟大家分享你的文章,欢迎投稿~

    ┏(^0^)┛明天见! 返回搜狐,查看更多

    责任编辑: