Some time ago, I optimized the company project by using ViewPager to switch between left and right pages, which makes interaction smoother. After the change, I found that the Fragment was not reused, and the onCreateView() and onDestroyView() life cycles were repeated every time I switched back. OnCreateView () returns viewbinding.getroot () without null because the project is using a ViewBinding. I’ve been looking at Android Jetpack’s ViewPager2 for the past few days and I’ve noticed that there’s an extra FragmentStateAdapter that’s a lot easier to use than before:
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 = }} 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 } }Copy the code
And then excitedly to the project schedule, feel like I’m keeping up with the big guy, But inadvertently see didn’t rewrite go onCreateView () and onDestroyView () life cycle (incidentally FragmentManager. Similar Activi FragmentLifecycleCallbacks can monitor fragments life cycle Ty life-cycle monitoring Application. ActivityLifecycleCallbacks), Based on the principle of trivial pursuit under the details about the FragmentPagerAdapter, FragmentStatePagerAdapter and FragmentStateAdapter difference.
FragmentPagerAdapter
Source from AndroidX. fragment, inherited from PagerAdapter, deprecated on AndroidX. fragment 1.3.0, focus on 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? // Does the fragment already exist? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); // Attach (fragment) has been executed. if (fragment ! = null) { if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment); mCurTransaction.attach(fragment); } else {// getItem() does not exist get 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; }Copy the code
You can see that the processing logic of the FragmentPagerAdapter is that there is a Fragment and then attach() is attached and it’s going to redo the lifecycle of the Fragment, and it’s not going to reuse the Fragment that was created before, which is obviously not what we want, This time we will use the FragmentStatePagerAdapter
FragmentStatePagerAdapter
Also from AndroidX. fragment, inherited from PagerAdapter, deprecated on AndroidX. fragment 1.3.0, again let’s look at 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; }Copy the code
Can see FragmentStatePagerAdapter have cache fragments queue, when fragments instantiation would pick up has been instantiated fragments, and the logic of a SavedState, used to hold the state of the fragments (after the slide will save the current interface, And the next interface and the previous interface (if any), save up to 3, others will be destroyed, Can be set via the ViewPager’s setOffscreenPageLimit(int Limit)) but will call back onSaveInstanceState(Bundle) before calling back onDestroy() OnCreate (Bundle savedInstanceState) to save the state of the Fragment. OnCreate (Bundle savedInstanceState) FragmentStatePagerAdapter fit in with the page more.
FragmentStateAdapter
From ViewPager2 is FragmentPagerAdapter and FragmentStatePagerAdapter replace class, inheritance in RecyclerView. Adapter, similar to FragmentStatePagerAdapter, This adapter implements a StatefulAdapter for holding the state of the 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);
}
}
Copy the code
You can see that the FragmentStateAdapter also has a cached Fragment queue