ViewPager2自定义Adapter解决Fragment动态增加和删除,不刷新等问题

一般的,我们使用ViewPager2是不太会整体地动态更新ViewPager的Fragment或者删除。我们一般只是固定了ViewPager,然后内部的每一个独立的Fragment自行处理各种逻辑。

而现在有个需求需要动态增加,删除,改变Fragment。

为了达到动态更新,并且希望是尽量复用Fragment。而不是每次都notifyDataSetChange。而且即使notifyDataSetChange也不一定能生效。
主要原因是默认的FragmentStateAdapter的实现中有如下描述:

    /**
     * Default implementation works for collections that don't add, move, remove items.
     * TODO(b/122670460): add lint rule
     * When overriding, also override {@link #containsItem(long)}.
     */
    @Override
    public long getItemId(int position) {
        return position;
    }

可以看出,他就是不希望你去改变。如果要改变,todo。你就需要自行实现。
网上帖子也一大堆,主要原因是FragmentStateAdapter的实现中,用position来标记Fragment作为不变的理由。虽然数据更新了,但是position不变则不会创建新的。

也就是因为getItemId和containsItem进行了处理解决问题。

这里我也编写了自己的框架。
抄过去,自行修改即可。

其中ReflectUtils获取父类的私有对象的反射,网上一大堆,找不到,请回复。
asOrNull则是强转。

特点

使用简单

  1. 让你的Fragment实现我的接口IFragmentNeedUpdate,回调函数会在onStart的时候或者动态更新的时候都会回调。因此,你不需要费力找地方保存每一个bean,然后在onCreateView等初始化,统统不需要。只需要在onNeedUpdate回调函数中里面编写UI更新代码即可。
  2. 使用本Adapter设置给ViewPager2,当然你也可以继承,干别的事情。
  3. 不再主动调用notifyXXXChange,调用submitData(list)初始化或者更新数据。
  4. 在submitData之前,新建Adapter的时候,设置fragmentCreator和differComparator。
    一般fragmentCreator,直接调用你的XXXFragment()新建对象即可。而differComparator用于判断2个数据bean对象是否相同。只要有数据有变化,则返回true即可。
    参考:
    val ada = NeedUpdateFragmentStateAdapter<XXXBean>(this)
    ada.differComparator = { a, b->
         var ret = false
         if (a.deviceId != b.deviceId) {
             ret = true
         } else if (a.name != b.name) {
             ret = true
         } else if (!xxxxxxxxxxx) {
             ret = true
         }
         ret
     }
    
     ada.fragmentCreator = { _ ->
         XXXXViewPageFragment()
     }
    
     mBinding.viewPager.adapter = ada
    

自定义NeedUpdateFragmentStateAdapter搞定

  1. 搞定了差异化更新。性能更优。
    根据是否differComparator来判断是否有差异抉择是否该刷新已经存在的Fragment,靠前的根据顺序更改;或者新的插入,从而减少大量更新。
  2. 搞定了compareItem,getItemId的逻辑问题
  3. 统一了onNeedUpdate的接口回调,简化Fragment的实现。

import androidx.fragment.app.Fragment
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.viewpager2.adapter.FragmentStateAdapter
import java.lang.Integer.min
import java.util.concurrent.atomic.AtomicLong

/**
 * @author allan
 * Description 用于需要更新Fragment的ViewPager2的Adapter。给你的Fragment实现IFragmentNeedUpdate,
 * 会在生命周期onStart自动回调或者有动态更新的时候通知。因此,不需要编写data和onCreateView的代码。
 */
class NeedUpdateFragmentStateAdapter<T>(fragment: Fragment) : FragmentStateAdapter(fragment) {
    private var datas: ArrayList<T> = ArrayList()
    //与datas对应的Id
    private var myIds:ArrayList<Long> = ArrayList()

    private val idGen:AtomicLong = AtomicLong(0)
    private val nextId
        get() = idGen.incrementAndGet()

    //如果你的是android.util.LongSparseArray自行调整。
    private var mFragmentsParent:androidx.collection.LongSparseArray<Fragment>? = null
    private fun mFragmentsGet() : androidx.collection.LongSparseArray<Fragment> {
        val mFragments = mFragmentsParent
        if (mFragments == null) {
            mFragmentsParent = ReflectionUtils.iteratorGetPrivateFieldValue(this, "mFragments").asOrNull<androidx.collection.LongSparseArray<Fragment>>()
            return mFragmentsParent!!
        }
        return mFragments
    }

    /**
     * 数据比较器。如果只要出现需要更新的任意局部数据,都返回true。不需要更新则是false。
     */
    var differComparator:((d1:T, d2:T)->Boolean)? = null

    /**
     * Fragment构建器。根据data来创建。
     * 请创建出Fragment,实现IFragmentNeedUpdate。
     */
    var fragmentCreator:((data:T)->IFragmentNeedUpdate<T>)? = null

    fun submitData(newDatas:List<T>) {
        if (fragmentCreator == null) {
            throw RuntimeException("you have not set fragmentCreator.")
        }
        if (differComparator == null) {
            throw RuntimeException("you have not set differComparator.")
        }

        if (idGen.get() == 0L) {
            initData(newDatas)
        } else {
            updateData(newDatas)
        }
    }

    /**
     * 初始化数据
     */
    private fun initData(datas:List<T>) {
        this.datas.clear()
        this.myIds.clear()
        this.datas.addAll(datas)
        var sz = datas.size
        while (sz-->0) {
            myIds.add(nextId)
        }

        notifyDataSetChanged()
    }

    //完全按照新列表,顺序来显示。
    private fun updateData(newDatas:List<T>) {
        val minSize = min(datas.size, newDatas.size)
        val deltaSize = kotlin.math.abs(datas.size - newDatas.size)
        //更新前面的Fragment
        for (i in 0 until minSize) {
            val newData = newDatas[i]
            if (differComparator?.invoke(datas[i], newData) == true) {
                //现在就存在的Fragment,我们需要更新它
                //先拿到老id进行更新
                mFragmentsGet().get(myIds[i]).asOrNull<IFragmentNeedUpdate<T>>()?.onNeedUpdate(newData)
                myIds[i] = nextId //更新id。放到后面,否则提取不匹配。
            }
            datas[i] = newData //不论如何都要换新数据的。
        }

        //处理后面的数据
        if (newDatas.size < datas.size) {
            var delta = deltaSize
            while(delta-- > 0) {
                datas.removeLast()
                myIds.removeLast()
            }
            notifyItemRangeRemoved(minSize, deltaSize)
        } else if (newDatas.size > datas.size) {
            var delta = deltaSize
            var min = minSize
            while (delta-- > 0) {
                datas.add(newDatas[min++])
                myIds.add(nextId)
            }
            notifyItemRangeInserted(minSize, deltaSize)
        }
    }

    override fun getItemCount(): Int {
        return datas.size
    }

    override fun createFragment(position: Int): Fragment {
        val data = datas[position]
        val fc = fragmentCreator?.invoke(data)
        val fragment = fc as Fragment
        fragment.lifecycle.addObserver(DataLifecycleObserver(data))
        return fragment
    }

    override fun containsItem(itemId: Long): Boolean {
        return myIds.contains(itemId)
    }

    override fun getItemId(position: Int): Long {
        return myIds[position]
    }

    /**
     * 请将你的Fragment实现它。将会通知你更新
     */
    interface IFragmentNeedUpdate<T> {
        fun onNeedUpdate(data:T)
    }

    class DataLifecycleObserver<T>(private val data:T) : DefaultLifecycleObserver {
        private var isInit = false

        override fun onStart(owner: LifecycleOwner) {
            if (!isInit && owner is IFragmentNeedUpdate<*>) {
                isInit = true
                val f = owner as IFragmentNeedUpdate<T>
                f.onNeedUpdate(data)
            }
        }
    }

}