android - FragmentPagerAdapter 和 FragmentStatePagerAdapter

标签 android performance android-viewpager fragmentpageradapter fragmentstatepageradapter

我目前正在考虑应该使用哪种 PagerAdapter 实现。 我遇到了与他们两个有关的困境。让我告诉你这些是什么。

1# FragmentPagerAdapter

工作正常,它会在以前的实例不可用时创建新的 fragment 实例,并在有这种机会时检索以前的 fragment 。

我最近在 StackOverflow 上读到,方法 PagerAdapter 的 getItem() 方法仅在需要创建 fragment 时被调用,但是 - 它被一遍又一遍地调用而且我必须处理创建新实例和检索此方法体内的旧实例。

但是 - 只调用了很少的回调和生命周期方法。例如,我无法设法调用 onSaveInstanceState。所以没有办法保存 fragment 的状态 - 当然我可以使用 SharedPreferences 或其他东西,但我想使用回调方法。有什么办法可以实现吗?

2#FragmentStatePagerAdapter

完美运行,保存 ViewPager 持有的每个 fragment 的状态。

但是 - 这个 PagerAdapter 总是创建新 fragment 。我在构造函数中检查了它。

那不是效率低下吗?我在 Google I/O Material 上看到 Romain Guy 说创建新的 Views 效率不高,尤其是当我们创建大量 Views 时,比如 ListView所以我们使用 convertView 来检索现有的 View 并根据需要多次更改它以达到我们的目的。所以在页面之间翻转非常相似——许多新的 Views——因为 fragment 是某种 View

在这两个 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 第 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 第 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代码:

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

最佳答案

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

我应该在那里调用我的 getFragmentAt() 方法,而不是 getItem(),整个回调应该看起来像那样。

@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 将因此被抛出。

关于android - FragmentPagerAdapter 和 FragmentStatePagerAdapter,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16598732/

相关文章:

java - 如何将图像从 Android 上传到 Google Cloud (Java)?

android - 处理大接口(interface)

performance - 浏览器 gzip 解压开销/速度

Android:处理 ViewPager 和数据密集型 fragment 的正确方法?

java - Android等待picasso完成

android - 基于 ListItem 特定变量在 ListItem 上膨胀布局

performance - PooledCloseableHttpClient 影响性能吞吐量

performance - Azure 网站上的文件系统是分布式的吗?

android - 如何在 Robolectric 的 ViewPager 上模拟拖动事件?

android - 没有迹象表明 NPE 可能发生在哪里