Pull to refresh
VK
Building the Internet

Android ViewPager. Как заменить один фрагмент на другой

Reading time 3 min
Views 20K
В этой статье хочу поделиться опытом использования 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 следующую функцию:

 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
Tags:
Hubs:
+29
Comments 2
Comments Comments 2

Articles

Information

Website
vk.com
Registered
Founded
Employees
5,001–10,000 employees
Location
Россия
Representative
Миша Берггрен