FragmentPagerAdapter 和 FragmentStatePagerAdapter

4
我正在考虑使用哪种PagerAdapter的实现方式。我对它们都有些疑虑,让我向您展示一下。
1# FragmentPagerAdapter 运行良好,当没有先前的实例可用时,它会创建新的片段实例,并在有机会时检索先前的片段。
我最近在StackOverflow上读到,只有在需要创建片段时才会调用PagerAdapter的getItem()方法,但是-它会一遍又一遍地调用,我必须在此方法的主体中处理创建新实例和检索旧实例。
但是-只有少数回调和生命周期方法被调用。例如,我无法管理onSaveInstanceState的调用。因此,无法保存片段的状态-当然,我可以使用SharedPreferences或其他东西,但我想使用回调方法。有什么办法可以做到这一点吗?
2# FragmentStatePagerAdapter 完美运行,保存ViewPager持有的每个片段的状态。
但是-这个PagerAdapter总是创建新的片段。我在构造函数中进行了检查。
那不是很低效吗?我在Google I/O材料上看到Romain Guy说,创建新视图并不高效,特别是当我们像在ListView中一样创建大量视图时,我们使用convertView来检索现有视图并将其更改为我们的目的,而且可以根据需要多次执行此操作。因此,在页面之间翻转时非常相似-许多新视图-因为片段是某种视图
在两个PagerAdapters中,我尝试了覆盖destroyItem()方法的技巧,但它根本不起作用。
这是我的问题。
我该怎么做?
我应该使用SharedPreferences和选项#1与FragmentPagerAdapter还是选项#2与FragmentStatePagerAdapter?
有没有可能我在这些适配器中做错了什么,它们的行为不像我们期望的那样?
下面是我的代码分成“可读”部分
PagerAdapter部分#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第二部分:

    /**
     * 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第三部分:

    @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 代码:

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;
}
}
1个回答

4

我在onPageSelectedMethod中搞砸了——我强制FragmentStatePagerAdapter调用getItem方法,每次页面改变时都至少实例化一次Fragment。这就是为什么我抱怨每次页面改变时都要实例化Fragment :)

相反,我应该在那里调用我的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异常。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接