В этой статье хочу поделиться опытом использования ViewPager и FragmentStatePagerAdapter, появившихся в compatibility package. Точнее, рассказать, с какими проблемами пришлось столкнуться и как они были решены. В частности, замена одного фрагмента на другой.
Вдохновившись постом в официальном блоге android-developers.blogspot.com/2011/08/horizontal-view-swiping-with-viewpager.html, стал пробовать. Суть задачи заключалась в том, чтобы в качестве одной из page были фрагменты почтового приложения, другими page’ами – фрагменты приложения инстант-мессенджера.
Фрагментов почтового приложения несколько, но page для их отображения только один. Для простоты рассмотрим два почтовых фрагмента — списки папок и писем в выбранной папке. Первым пользователь видит фрагмент списка папок. При выборе конкретной папки фрагмент папок замещается соответствующим фрагментом писем в том же page.
Но, как и многие (стоит поискать в Интернете по запросу «android viewpager replace fragments» ), я столкнулся с проблемой — один Fragment в случае использования ViewPager не заменялся на другой.
Решением стало следующее — перенести код ViewPager, PagerAdapter и FragmentStatePagerAdapter из исходных кодов compatibility pack в свой проект и добавить в ViewPager следующую функцию:
А во FragmentStatePagerAdapter добавить:
Функция handleGetItemInbalidated() реализуется в вашем классе, наследуемом от FragmentStatePagerAdapter. После ее вызова функция FragmentStatePagerAdapter .getItem() должна возвращать newFragment. getFragmentPosition() также реализуется в наследуемом классе и возвращает индекс фрагмента в списке page’а.
Теперь замена фрагментов делается так:
Обращаю внимание на то, что FragmentStatePagerAdapter «выгружает» (fragmentTransaction.remove()) page’и, которые находятся дальше, чем на одну позицию от текущего. В моем приложении page с почтовым фрагментом выгружаться при пролистывании не должна.
Для этого нужно переопределить две функции в классе FragmentStatePagerAdapter:
Условие (position == getCount() – 1) обусловлено тем, что page с почтовым фрагментом всегда последний.
Скачать тестовый проект в Eclipse: files.mail.ru/I72IP7
Вдохновившись постом в официальном блоге android-developers.blogspot.com/2011/08/horizontal-view-swiping-with-viewpager.html, стал пробовать. Суть задачи заключалась в том, чтобы в качестве одной из page были фрагменты почтового приложения, другими page’ами – фрагменты приложения инстант-мессенджера.
Фрагментов почтового приложения несколько, но page для их отображения только один. Для простоты рассмотрим два почтовых фрагмента — списки папок и писем в выбранной папке. Первым пользователь видит фрагмент списка папок. При выборе конкретной папки фрагмент папок замещается соответствующим фрагментом писем в том же page.
Но, как и многие (стоит поискать в Интернете по запросу «android viewpager replace fragments» ), я столкнулся с проблемой — один Fragment в случае использования ViewPager не заменялся на другой.
Решением стало следующее — перенести код ViewPager, PagerAdapter и FragmentStatePagerAdapter из исходных кодов compatibility pack в свой проект и добавить в ViewPager следующую функцию:
public void notifyItemChanged(Object oldItem, Object newItem) {
if (mItems != null) {
for (ItemInfo itemInfo : mItems) {
if (itemInfo.object.equals(oldItem)) {
itemInfo.object = newItem;
}
}
}
invalidate();
}
А во FragmentStatePagerAdapter добавить:
public void replaceFragmetns(ViewPager container, Fragment oldFragment, Fragment newFragment) {
// ensure getItem returns newFragemtn after calling handleGetItemInbalidated()
handleGetItemInbalidated(container, oldFragment, newFragment);
startUpdate(container);
// remove old fragment
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
int position = getFragmentPosition(oldFragment);
while (mSavedState.size() <= position) {
mSavedState.add(null);
}
mSavedState.set(position, null);
mFragments.set(position, null);
mCurTransaction.remove(oldFragment);
// add new fragment
while (mFragments.size() <= position) {
mFragments.add(null);
}
mFragments.set(position, newFragment);
mCurTransaction.add(container.getId(), newFragment);
finishUpdate(container);
container.notifyItemChanged(oldFragment, newFragment);
}
protected abstract void handleGetItemInbalidated(View container, Fragment oldFragment, Fragment newFragment);
protected abstract int getFragmentPosition(Fragment fragment);
Функция handleGetItemInbalidated() реализуется в вашем классе, наследуемом от FragmentStatePagerAdapter. После ее вызова функция FragmentStatePagerAdapter .getItem() должна возвращать newFragment. getFragmentPosition() также реализуется в наследуемом классе и возвращает индекс фрагмента в списке page’а.
Теперь замена фрагментов делается так:
mAdapter.replaceFragmetns(mViewPager, oldFragment, newFragment);
Обращаю внимание на то, что FragmentStatePagerAdapter «выгружает» (fragmentTransaction.remove()) page’и, которые находятся дальше, чем на одну позицию от текущего. В моем приложении page с почтовым фрагментом выгружаться при пролистывании не должна.
Для этого нужно переопределить две функции в классе FragmentStatePagerAdapter:
@Override
public Object instantiateItem(View container, int position) {
if (position == getCount() - 1) {
if (mMailCahcedFragment == null) {
return super.instantiateItem(container, position);
} else {
return mMailCahcedFragment;
}
} else {
return super.instantiateItem(container, position);
}
}
@Override
public void destroyItem(View container, int position, Object object) {
if (position == getCount() - 1) {
} else {
super.destroyItem(container, position, object);
}
}
Условие (position == getCount() – 1) обусловлено тем, что page с почтовым фрагментом всегда последний.
Скачать тестовый проект в Eclipse: files.mail.ru/I72IP7