ViewPager
的使用,除了用于View切换之外,还可用于Fragment
切换。
单独使用Fragment
经常会遇到一些问题,比如Fragment
切换时,显示和隐藏的Fragment
有时候会出现重叠,导致相互之间的UI和事件混乱。
结合ViewPager
的使用,则可以避免掉大部分的问题。Google基于PagerAdapter
添加了FragmentPagerAdapter
类来解决Fragment
与ViewPager
之间的配合问题。
FragmentPagerAdapter
的源码非常简单,我们可以了解下。
首先,FragmentPagerAdapter
添加了三个全局变量:
private final FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction = null;
private Fragment mCurrentPrimaryItem = null;
- mFragmentManager 用于管理
Fragment
对象 - mCurTransaction 当前操作事务
- mCurrentPrimaryItem 当前主要项
同时,FragmentPagerAdapter
也添加了几个方法:
/**
* 获取给定位置对应的Fragment。
*
* @param position 给定的位置
* @return 对应的Fragment
*/
public abstract Fragment getItem(int position);
/**
* 获取给定位置的项Id,用于生成Fragment名称
*
* @param position 给定的位置
* @return 项Id
*/
public long getItemId(int position) {
return position;
}
/**
* 根据viewId和项Id生成Fragment名称
* @param viewId
* @param id
* @return Fragment名称
*/
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
FragmentPagerAdapter
重载了以下几个方法:
@Override
public void startUpdate(ViewGroup container) {
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
// 创建新事务
mCurTransaction = mFragmentManager.beginTransaction();
}
// 获取单项的Id
final long itemId = getItemId(position);
// 根据View的Id和单项Id生成名称
String name = makeFragmentName(container.getId(), itemId);
// 根据生成的名称获取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));
}
// 非当前主要项,需要隐藏相关的菜单及信息
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
if (mCurTransaction == null) {
// 创建新事务
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) {
// 主要项切换,相关菜单及信息进行切换
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) {
// 提交事务
mCurTransaction.commitAllowingStateLoss();
mCurTransaction = null;
// 立即运行等待中事务
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) {
}
我们可以看到,事务的创建是在instantiateItem
和destroyItem
方法中,而事务的提交,则是在finishUpdate
方法中。这是因为ViewPager
有预加载的功能,将多个Fragment
的操作放置在同一个事务中进行,然后在ViewPager
中加载完成后进行统一提交。
同样我们也注意到一点,在instantiateItem
方法中,主要的操作是将Fragment
添加到FragmentManager
中,已添加到FragmentManager
中的Fragment
,则只进行attach操作。而在destroyItem
方法中,则只是进行detach操作。detach操作并不会将Fragment
移除,Fragment
依旧是由FragmentManager
进行管理。
我们可以创建个简单应用,并且按照以下逻辑进行操作,查看相关LOG,来进一步了解:
①首次进入默认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
②点击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
③点击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
④点击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
⑤点击Fragment5
:
I: Fragment4 setUserVisibleHint false
I: Fragment5 setUserVisibleHint true
I: Fragment3 onPause
I: Fragment3 onStop
I: Fragment3 onDestroyView
⑥进入后台:
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
⑦返回前台:
I: Fragment4 onStart
I: Fragment5 onStart
I: NormalPagerActivity onStart
I: NormalPagerActivity onResume
I: Fragment4 onResume
I: Fragment5 onResume
⑧点击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
通过LOG,可以进一步了解:
- 第①步时,
Fragment1
的setUserVisibleHint true
方法调用时间早于onCreateView
方法。 - 每个
Fragment
在FragmentPagerAdapter
类的destroyItem
方法中被detach后,只被调用到了onPause
、onStop
和onDestroyView
方法,onDestroy
和onDetach
方法并未被调用到。 - 第⑥步和第⑦步显示,
Fragment
只有在attach后,才会受到Activity生命周期的影响,调用到自己的生命周期。 - 第⑧步显示,
Fragment3
并未调用onAttach
方法,而是直接调用到onCreateView
方法,直接重建了UI来显示。