前段時間在給公司項目做優化,換用了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()
@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?
//這個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()
裡的實作
@NonNull
@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隊列