4

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;
}
}
4

1 回答 1

4

我搞砸了onPageSelectedMethod- 我强迫 FragmentStatePagerAdapter 调用getItem- 至少实例化 Fragment 一次 - 每当页面更改时的方法。这就是为什么我抱怨每次页面更改时都实例化 Fragment :)

而不是getItem()我应该调用我的getFragmentAt()方法,整个回调应该是这样的。

@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");
}

虽然它工作正常。仍然可能存在风险,Fragment将无法找到并且该方法将返回 null - 然后将抛出 NPE。

于 2013-05-17T09:04:51.623 回答