ViewPager使用詳解(二):FragmentPagerAdapter

ViewPager的使用,除了用于View切換之外,還可用于Fragment切換。

單獨(dú)使用Fragment經(jīng)常會(huì)遇到一些問(wèn)題,比如Fragment切換時(shí),顯示和隱藏的Fragment有時(shí)候會(huì)出現(xiàn)重疊,導(dǎo)致相互之間的UI和事件混亂。

結(jié)合ViewPager的使用,則可以避免掉大部分的問(wèn)題。Google基于PagerAdapter添加了FragmentPagerAdapter類(lèi)來(lái)解決FragmentViewPager之間的配合問(wèn)題。

FragmentPagerAdapter的源碼非常簡(jiǎn)單,我們可以了解下。

首先,FragmentPagerAdapter添加了三個(gè)全局變量:

private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
  • mFragmentManager 用于管理Fragment對(duì)象
  • mCurTransaction 當(dāng)前操作事務(wù)
  • mCurrentPrimaryItem 當(dāng)前主要項(xiàng)

同時(shí),FragmentPagerAdapter也添加了幾個(gè)方法:

/**
* 獲取給定位置對(duì)應(yīng)的Fragment。
*
* @param position 給定的位置
* @return 對(duì)應(yīng)的Fragment
*/
public abstract Fragment getItem(int position);

/**
 * 獲取給定位置的項(xiàng)Id,用于生成Fragment名稱(chēng)
 *
 * @param position 給定的位置
 * @return 項(xiàng)Id
 */
public long getItemId(int position) {
        return position;
}

/**
 * 根據(jù)viewId和項(xiàng)Id生成Fragment名稱(chēng)
 * @param viewId
 * @param id
 * @return Fragment名稱(chēng)
 */
private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
}

FragmentPagerAdapter重載了以下幾個(gè)方法:

@Override
public void startUpdate(ViewGroup container) {
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    if (mCurTransaction == null) {
      // 創(chuàng)建新事務(wù)
      mCurTransaction = mFragmentManager.beginTransaction();
    }

    // 獲取單項(xiàng)的Id
    final long itemId = getItemId(position);

    // 根據(jù)View的Id和單項(xiàng)Id生成名稱(chēng)
    String name = makeFragmentName(container.getId(), itemId);
    // 根據(jù)生成的名稱(chēng)獲取FragmentManager中的Fragment
    Fragment fragment = mFragmentManager.findFragmentByTag(name);
    if (fragment != null) {
      if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
      // 如果Fragment已被添加到FragmentManager中,則只需要附著到Activity
      mCurTransaction.attach(fragment);
    } else {
      // 如果Fragment未被添加到FragmentManager中,則先獲取,再添加到Activity中
      fragment = getItem(position);
      if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
      mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
    }
    // 非當(dāng)前主要項(xiàng),需要隱藏相關(guān)的菜單及信息
    if (fragment != mCurrentPrimaryItem) {
      fragment.setMenuVisibility(false);
      fragment.setUserVisibleHint(false);
    }

    return fragment;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    if (mCurTransaction == null) {
      // 創(chuàng)建新事務(wù)
      mCurTransaction = mFragmentManager.beginTransaction();
    }
    if (DEBUG)
      Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object + " v=" + ((Fragment) object).getView());
    // 將Fragment移出UI,但并未從FragmentManager中移除
    mCurTransaction.detach((Fragment) object);
}

@Override
public void setPrimaryItem(ViewGroup container, int position, Object object) {
    Fragment fragment = (Fragment) object;
    if (fragment != mCurrentPrimaryItem) {
      // 主要項(xiàng)切換,相關(guān)菜單及信息進(jìn)行切換
      if (mCurrentPrimaryItem != null) {
        mCurrentPrimaryItem.setMenuVisibility(false);
        mCurrentPrimaryItem.setUserVisibleHint(false);
      }
      if (fragment != null) {
        fragment.setMenuVisibility(true);
        fragment.setUserVisibleHint(true);
      }
      mCurrentPrimaryItem = fragment;
    }
}

@Override
public void finishUpdate(ViewGroup container) {
    if (mCurTransaction != null) {
      // 提交事務(wù)
      mCurTransaction.commitAllowingStateLoss();
      mCurTransaction = null;
      // 立即運(yùn)行等待中事務(wù)
      mFragmentManager.executePendingTransactions();
    }
}

@Override
public boolean isViewFromObject(View view, Object object) {
    return ((Fragment) object).getView() == view;
}

@Override
public Parcelable saveState() {
    return null;
}

@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}

我們可以看到,事務(wù)的創(chuàng)建是在instantiateItemdestroyItem方法中,而事務(wù)的提交,則是在finishUpdate方法中。這是因?yàn)?code>ViewPager有預(yù)加載的功能,將多個(gè)Fragment的操作放置在同一個(gè)事務(wù)中進(jìn)行,然后在ViewPager中加載完成后進(jìn)行統(tǒng)一提交。

同樣我們也注意到一點(diǎn),在instantiateItem方法中,主要的操作是將Fragment添加到FragmentManager中,已添加到FragmentManager中的Fragment,則只進(jìn)行attach操作。而在destroyItem方法中,則只是進(jìn)行detach操作。detach操作并不會(huì)將Fragment移除,Fragment依舊是由FragmentManager進(jìn)行管理。

我們可以創(chuàng)建個(gè)簡(jiǎn)單應(yīng)用,并且按照以下邏輯進(jìn)行操作,查看相關(guān)LOG,來(lái)進(jìn)一步了解:

①首次進(jìn)入默認(rèn)Fragment1:

    I: NormalPagerActivity onCreate null
    I: NormalPagerActivity onStart
    I: NormalPagerActivity onResume
    I: Fragment1 setUserVisibleHint false
    I: Fragment2 setUserVisibleHint false
    I: Fragment1 onAttach
    I: Fragment1 onCreate null
    I: Fragment1 setUserVisibleHint true
    I: Fragment2 onAttach
    I: Fragment2 onCreate null
    I: Fragment2 onCreateView null
    I: Fragment2 onViewCreated
    I: Fragment2 onActivityCreated null
    I: Fragment2 onViewStateRestored null
    I: Fragment1 onCreateView null
    I: Fragment1 onViewCreated
    I: Fragment1 onActivityCreated null
    I: Fragment1 onViewStateRestored null
    I: Fragment1 onStart
    I: Fragment1 onResume
    I: Fragment2 onStart
    I: Fragment2 onResume

②點(diǎn)擊Fragment2:

    I: Fragment3 setUserVisibleHint false
    I: Fragment1 setUserVisibleHint false
    I: Fragment2 setUserVisibleHint true
    I: Fragment3 onAttach
    I: Fragment3 onCreate null
    I: Fragment3 onCreateView null
    I: Fragment3 onViewCreated
    I: Fragment3 onActivityCreated null
    I: Fragment3 onViewStateRestored null
    I: Fragment3 onStart
    I: Fragment3 onResume

③點(diǎn)擊Fragment3:

    I: Fragment4 setUserVisibleHint false
    I: Fragment2 setUserVisibleHint false
    I: Fragment3 setUserVisibleHint true
    I: Fragment4 onAttach
    I: Fragment4 onCreate null
    I: Fragment1 onPause
    I: Fragment1 onStop
    I: Fragment1 onDestroyView
    I: Fragment4 onCreateView null
    I: Fragment4 onViewCreated
    I: Fragment4 onActivityCreated null
    I: Fragment4 onViewStateRestored null
    I: Fragment4 onStart
    I: Fragment4 onResume

④點(diǎn)擊Fragment4:

    I: Fragment5 setUserVisibleHint false
    I: Fragment3 setUserVisibleHint false
    I: Fragment4 setUserVisibleHint true
    I: Fragment5 onAttach
    I: Fragment5 onCreate null
    I: Fragment2 onPause
    I: Fragment2 onStop
    I: Fragment2 onDestroyView
    I: Fragment5 onCreateView null
    I: Fragment5 onViewCreated
    I: Fragment5 onActivityCreated null
    I: Fragment5 onViewStateRestored null
    I: Fragment5 onStart
    I: Fragment5 onResume

⑤點(diǎn)擊Fragment5:

    I: Fragment4 setUserVisibleHint false
    I: Fragment5 setUserVisibleHint true
    I: Fragment3 onPause
    I: Fragment3 onStop
    I: Fragment3 onDestroyView

⑥進(jìn)入后臺(tái):

    I: Fragment4 onPause
    I: Fragment5 onPause
    I: NormalPagerActivity onPause
    I: Fragment2 onSaveInstanceState Bundle[{}]
    I: Fragment1 onSaveInstanceState Bundle[{}]
    I: Fragment3 onSaveInstanceState Bundle[{}]
    I: Fragment4 onSaveInstanceState Bundle[{}]
    I: Fragment5 onSaveInstanceState Bundle[{}]
    I: NormalPagerActivity onSaveInstanceState Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@878ae08, 2131492956=android.view.AbsSavedState$1@878ae08, 2131492957=android.view.AbsSavedState$1@878ae08, 2131492958=android.support.v7.widget.Toolbar$SavedState@298b9a1, 2131492959=android.view.AbsSavedState$1@878ae08, 2131492976=FragmentPager.SavedState{71c47c6 position=4}, 2131492977=HorizontalScrollView.SavedState{e0d1387 scrollPosition=0}}}], android:support:fragments=android.support.v4.app.FragmentManagerState@8eea2b4}]
    I: Fragment4 onStop
    I: Fragment5 onStop
    I: NormalPagerActivity onStop

⑦返回前臺(tái):

    I: Fragment4 onStart
    I: Fragment5 onStart
    I: NormalPagerActivity onStart
    I: NormalPagerActivity onResume
    I: Fragment4 onResume
    I: Fragment5 onResume

⑧點(diǎn)擊Fragment4:

    I: Fragment3 setUserVisibleHint false
    I: Fragment5 setUserVisibleHint false
    I: Fragment4 setUserVisibleHint true
    I: Fragment3 onCreateView null
    I: Fragment3 onViewCreated
    I: Fragment3 onActivityCreated null
    I: Fragment3 onViewStateRestored null
    I: Fragment3 onStart
    I: Fragment3 onResume

通過(guò)LOG,可以進(jìn)一步了解:

  1. 第①步時(shí),Fragment1setUserVisibleHint true方法調(diào)用時(shí)間早于onCreateView方法。
  2. 每個(gè)FragmentFragmentPagerAdapter類(lèi)的destroyItem方法中被detach后,只被調(diào)用到了onPause、onStoponDestroyView方法,onDestroyonDetach方法并未被調(diào)用到。
  3. 第⑥步和第⑦步顯示,Fragment只有在attach后,才會(huì)受到Activity生命周期的影響,調(diào)用到自己的生命周期。
  4. 第⑧步顯示,Fragment3并未調(diào)用onAttach方法,而是直接調(diào)用到onCreateView方法,直接重建了UI來(lái)顯示。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容