I am currently thinking which implementation of PagerAdapter should I use. I've got dilemmas connected to both of them. Let me show you what are these.
1# FragmentPagerAdapter
Works fine, It creates new instances of fragments when none previous instances are available and retrieves previously fragments when there's such opportunity.
I've read recently on StackOverflow that, method PagerAdapter's
getItem()
method is called ONLY when it need to create fragment, but - it is called over and over and I had to handle creating new instances and retrieving old ones inside this method's body.
BUT - Only few callback and life-cycle methods are called. For example I can't manage to onSaveInstanceState
to be called. So there's no way to save fragment's state - of course I can use SharedPreferences
or something else, but I wanted to use callback methods. Is there any way to accomplish that?
2# FragmentStatePagerAdapter
Works perfectly, saves state of every fragment that ViewPager
holds.
BUT - this PagerAdapter
ALWAYS creates new fragment. I checked it inside constructor.
Isn't that inefficient? I saw Romain Guy on Google I/O materials saying that creating new Views
is not efficient, especially when we creating lots of Views
like in ListView
so we use convertView
to retrieve existing View
and change it for our purposes as many times as we want to. So fliping between pages is quite similar - many new Views
- because fragment is a some kind of View
In both PagerAdapters
I tried trick with Overriding destroyItem()
method but It didn't work at all.
And here's my question.
What should I do?
Should I use SharedPreferences
and option #1 with FragmentPagerAdapter
or option #2 with FragmentStatePagerAdapter
?
Is there any possibility, that I do something wrong within these adapters, to they behave not as we expect to?
Below is my code divided into "readable" parts
PagerAdapter part #1:
/**
* Adapter class to {@link WizardPager}
*/
public static class WizardCrazyAdapter extends FragmentStatePagerAdapter
implements OnPageChangeListener{
public static final String tag = "android:switcher:"+R.id.pager_w+":";
/**
* Refernece to root activity
*/
WizardActivity wizardActivity;
/**
* list of fragments
*/
private final ArrayList<FragmentInfo> fInfos = new ArrayList<FragmentInfo>();
private short prevPageNumber = 0;
/**
* Constructor of adapter
* @param wizardActivity
* {@link WizardActivity} as reference to activity root
*/
public WizardCrazyAdapter(WizardActivity wizardActivity) {
super(wizardActivity.getSupportFragmentManager());
this.wizardActivity = wizardActivity;
}
static final class FragmentInfo {
private final Class<?> _clss;
private Bundle _args;
public FragmentInfo(Class<?> clss, Bundle args) {
_clss =clss;
_args =args;
}
}
public void addPage(Class<?> clss, Bundle args){
FragmentInfo fi = new FragmentInfo(clss, args);
fInfos.add(fi);
}
/**
* Return number of pages
*/
public int getCount() {
return fInfos.size();
}
PagerAdapter part #2:
/**
* Searches in {@link FragmentManager} for {@link Fragment} at specified position
* @param position
* @return
*/
private AbstractWizardFragment getFragmentAt(int position){
FragmentManager fm = wizardActivity.getSupportFragmentManager();
AbstractWizardFragment awf = (AbstractWizardFragment) fm.findFragmentByTag(tag+position);
return awf;
}
/**
* Return page of view pager
*/
@Override
public Fragment getItem(int position) {
/*finding existing instance of fragment*/
AbstractWizardFragment awf = getFragmentAt(position);
if(awf == null){
/*creating new instance if no instance exist*/
Log.v("WizardActivity", "creating new Fragment");
FragmentInfo fi = fInfos.get(position);
awf = (AbstractWizardFragment) Fragment.instantiate(wizardActivity, fi._clss.getName());
}else{
Log.v("WizardActivity", "found existing Fragment");
}
return awf;
}
PagerAdapter part #3:
@Override
public void onPageSelected(int pageNumber) {
wizardActivity.stepFragment.setCurrentStepAndChangeText(pageNumber);
if(pageNumber != prevPageNumber){
AbstractWizardFragment prevFragment = (AbstractWizardFragment) getItem(prevPageNumber);//TODO change if any problems
prevFragment.onDetachedFromViewPager(wizardActivity.mForm);
}
AbstractWizardFragment currFragment = (AbstractWizardFragment) getItem(pageNumber);//TODO change if any problems
currFragment.onAttachedToViewPager(wizardActivity.mForm);
prevPageNumber = (short) pageNumber;
Log.d("WizardActivity", "onPageSelected");
}
@Override
public Object instantiateItem(ViewGroup arg0, int arg1) {
Log.d("WizardActivity", "instantiateItem "+arg1);
return super.instantiateItem(arg0, arg1);
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
// super.destroyItem(container, position, object);
Log.v("WizardActivity", "that would be destroy");
}
}
ViewPager code:
public class WizardPager extends ViewPager{
/**
* Flag to check if view pager must be scrolled
*/
protected boolean isScrollable;
/**
* Default constructor
* @param context {@link Context}
*/
public WizardPager(Context context) {
super(context);
isScrollable = true;
}
/**
* Standard constructor
* @param context {@link Context}
* @param attrs {@link AttributeSet}
*/
public WizardPager(Context context, AttributeSet attrs) {
super(context, attrs);
isScrollable = true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (this.isScrollable) {
return super.onTouchEvent(event);
}
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (this.isScrollable) {
return super.onInterceptTouchEvent(event);
}
return false;
}
/**
* Enable scroll of pages
*/
public void enableScroll(){
this.isScrollable = true;
}
/**
* Disable scroll of pages
*/
public void disableScroll(){
this.isScrollable = false;
}
/**
* Check if pages can be scrolled
* @return
*/
public boolean isScrollable(){
return isScrollable;
}
}
I messed up in onPageSelectedMethod
- I was forcing FragmentStatePagerAdapter to call getItem
- which instantiates Fragment at least once - method whenever page is changed. That's why I complained about instantiating Fragment every time page is changed :)
Instead of getItem()
I should call there my getFragmentAt()
method, and the whole callback should look like that.
@Override
public void onPageSelected(int pageNumber) {
wizardActivity.stepFragment.setCurrentStepAndChangeText(pageNumber);
if(pageNumber != prevPageNumber){
AbstractWizardFragment prevFragment = (AbstractWizardFragment) getFragmentAt(prevPageNumber);
prevFragment.onDetachedFromViewPager(wizardActivity.mForm);
}
AbstractWizardFragment currFragment = (AbstractWizardFragment) getFragmentAt(pageNumber);
currFragment.onAttachedToViewPager(wizardActivity.mForm);
prevPageNumber = (short) pageNumber;
Log.d("WizardActivity", "onPageSelected");
}
Although It works fine. There still might be risk, that Fragment
won't be found and the method will return null - and then consequently NPE will be thrown.