ViewPager adapter适配误区

2020-04-17  本文已影响0人  神棄丶Aria

一、简介

目前情况下我们在编写ViewPagerAdapter时会这样编码:

NormalPagerAdapter.java
class NormalPagerAdapter(
    private val fragmentList: List<Fragment>,
    fragmentManager: FragmentManager) : FragmentPagerAdapter(fragmentManager) {
    override fun getItem(position: Int): Fragment = fragmentList[position]

    override fun getCount(): Int = fragmentList.size
}

MainActivity.java
class ViewPagerActivity : AppCompatActivity() {

    private lateinit var normalAdapter : NormalPagerAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_pager)

        val list: List<Fragment> = listOf(MyFragment(), MyFragment())
        normalAdapter = NormalPagerAdapter(list, supportFragmentManager)

        viewPager.adapter = normalAdapter
    }
}

但是这种写法会造成在Activity被回收触发重建时,adapter.getItem()与viewPager的Fragment对应不上的问题。
触发重建的场景可以用开发者模式中的(不保留活动)功能模拟复现。

二、原因

首先看pagerAdapter的源码

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }

        final long itemId = getItemId(position);

        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }

        return fragment;
    }

当初始化item时PagerAdapter会先从FragmentManager中取fragment,如果fragment存在则不会走getItem()方法,也就是说Activity重建时我们会创建新的fragmentList,添加到ViewPager上的实例并非fragmentList中的实例,这就导致了pagerAdapter的Fragment与ViewPager上的Fragment实例不一致的问题。

三、解决方案

class BaseViewPagerAdapter(fragmentManager: FragmentManager, private val adapterInterface: PagerAdapterInterface) : FragmentPagerAdapter(fragmentManager) {

    private val registerFragments = SparseArray<Fragment>()

    override fun getItem(position: Int): Fragment = adapterInterface.getItem(position)

    override fun getCount(): Int = adapterInterface.getCount()

    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        val fragment: Fragment = super.instantiateItem(container, position) as Fragment
        registerFragments.put(position, fragment)
        return fragment
    }

    override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) {
        registerFragments.remove(position)
        super.destroyItem(container, position, `object`)
    }

    fun getRegisterFragment(position: Int) : Fragment? = registerFragments[position]
}

interface PagerAdapterInterface {
    fun getItem(position: Int) : Fragment

    fun getCount() : Int
}

将instantianteItem()中产生的fragment保存在registerFragments中,这样就保证了registerFragments中都为ViewPager上的Fragment。
外部通过adapter获取fragment的方法变为getRegisterFragment,使用时需要注意一点就是,通过该方法获取的fragment可能为空。

四、参考文章

https://mp.weixin.qq.com/s/MOWdbI5IREjQP1Px-WJY1Q

上一篇 下一篇

猜你喜欢

热点阅读