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可能为空。