前段时间在给公司项目做优化,换用了ViewPager,可以左右切换页面,交互更顺滑,改完之后发现Fragment总是不复用,每次切换回来会重复走onCreateView()
和onDestroyView()
生命周期???这咋能行呢,于是就在onCreateView()
做了简单的判断,因为项目用的是ViewBinding,所以就ViewBinding不为空就直接返回ViewBinding.getRoot()
了。这几天在看Android Jetpack的ViewPager2发现多了个FragmentStateAdapter
用法比之前简单不少:
class CollectionDemoFragment : Fragment() {
// When requested, this adapter returns a DemoObjectFragment,
// representing an object in the collection.
private lateinit var demoCollectionAdapter: DemoCollectionAdapter
private lateinit var viewPager: ViewPager2
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.collection_demo, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
demoCollectionAdapter = DemoCollectionAdapter(this)
viewPager = view.findViewById(R.id.pager)
viewPager.adapter = demoCollectionAdapter
}
}
//重点在这里 就重写了两个方法
class DemoCollectionAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
override fun getItemCount(): Int = 100
override fun createFragment(position: Int): Fragment {
// Return a NEW fragment instance in createFragment(int)
val fragment = DemoObjectFragment()
fragment.arguments = Bundle().apply {
// Our object is just an integer :-P
putInt(ARG_OBJECT, position + 1)
}
return fragment
}
}
然后兴冲冲的给项目安排上,感觉自己又跟上大佬的脚步了,可是无意间看到居然没有重写走onCreateView()
和onDestroyView()
生命周期(顺便提一下FragmentManager.FragmentLifecycleCallbacks
可以监控Fragment生命周期类似Activity生命周期监听Application.ActivityLifecycleCallbacks
),本着打破砂锅问到底的原则详细了解了下FragmentPagerAdapter、FragmentStatePagerAdapter和FragmentStateAdapter三者的区别。
FragmentPagerAdapter
源于androidx.fragment,继承于PagerAdapter
,已于androidx.fragment 1.3.0版本废弃,重点在instantiateItem()
@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?
//这个fragment已经存在过?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
//已经存在执行attach(fragment);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {//没有存在通过getItem()获取
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);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
} else {
fragment.setUserVisibleHint(false);
}
}
return fragment;
}
可以看到FragmentPagerAdapter
的处理逻辑是存在这个Fragment就会执行attach()
必然会重走一遍Fragment的生命周期,并不会复用之前创建的Fragment,显然跟我们要的不符,这个时候我们就用到了FragmentStatePagerAdapter
FragmentStatePagerAdapter
同样源于androidx.fragment,继承于PagerAdapter
,已于androidx.fragment 1.3.0版本废弃,同样我们也看一下instantiateItem()
里的实现
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
}
while (mFragments.size() <= position) {
mFragments.add(null);
}
fragment.setMenuVisibility(false);
if (mBehavior == BEHAVIOR_SET_USER_VISIBLE_HINT) {
fragment.setUserVisibleHint(false);
}
mFragments.set(position, fragment);
mCurTransaction.add(container.getId(), fragment);
if (mBehavior == BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
mCurTransaction.setMaxLifecycle(fragment, Lifecycle.State.STARTED);
}
return fragment;
}
可以看到FragmentStatePagerAdapter
有缓存Fragment队列,当Fragment实例化之后会取已经实例化的Fragment,并且多了个SavedState的逻辑,用于保存Fragment的状态(滑过后会保存当前界面,以及下一个界面和上一个界面(如果有),最多保存3个,其他会被销毁掉,可以通过ViewPager的setOffscreenPageLimit(int limit)
设置)
但在回调onDestroy()
方法之前会回调onSaveInstanceState(Bundle outState)
方法来保存Fragment的状态,下次Fragment显示时通过onCreate(Bundle savedInstanceState)
把存储的状态值取出来,FragmentStatePagerAdapter
比较适合页面比较多的情况。
FragmentStateAdapter
源于ViewPager2是FragmentPagerAdapter
和FragmentStatePagerAdapter
的替换类,继承于RecyclerView.Adapter
,
与FragmentStatePagerAdapter
相似,该适配器实现了StatefulAdapter
用于保存Fragment的状态。
private void ensureFragment(int position) {
long itemId = getItemId(position);
if (!mFragments.containsKey(itemId)) {
// TODO(133419201): check if a Fragment provided here is a new Fragment
Fragment newFragment = createFragment(position);
newFragment.setInitialSavedState(mSavedStates.get(itemId));
mFragments.put(itemId, newFragment);
}
}
可以看到FragmentStateAdapter
同样有缓存Fragment队列