按下返回按钮后发生Android ViewPager异常

4

我有一个ViewPager,其中包含3个片段,它可以很好地工作。我从我的任何一个视图页内的片段中启动一个活动,并且该活动被显示出来。之后当我按返回按钮时,我的应用程序会崩溃,并显示以下异常:

FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to resume activity {AppName.Activities/AppName.Activities.ViewPagerActivity}: java.lang.IndexOutOfBoundsException: Invalid index 2, size is 0
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2120)
    at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:2135)
    at android.app.ActivityThread$H.handleMessage(ActivityThread.java:957)
    at android.os.Handler.dispatchMessage(Handler.java:99)
    at android.os.Looper.loop(Looper.java:123)
    at android.app.ActivityThread.main(ActivityThread.java:3683)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:507)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
    at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.IndexOutOfBoundsException: Invalid index 2, size is 0
    at java.util.ArrayList.throwIndexOutOfBoundsException(ArrayList.java:257)
    at java.util.ArrayList.set(ArrayList.java:484)
    at android.support.v4.app.FragmentStatePagerAdapter.destroyItem(FragmentStatePagerAdapter.java:97)
    at android.support.v4.view.ViewPager.populate(ViewPager.java:415)
    at android.support.v4.view.ViewPager.setCurrentItemInternal(ViewPager.java:271)
    at android.support.v4.view.ViewPager.setCurrentItem(ViewPager.java:244)
    at AppName.Activities.ViewPagerActivity.setUpView(ViewPagerActivity.java:36)
    at AppName.Activities.ViewPagerActivity.onStart(ViewPagerActivity.java:28)
    at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1129)
    at android.app.Activity.performStart(Activity.java:3791)
    at android.app.Activity.performRestart(Activity.java:3821)
    at android.app.Activity.performResume(Activity.java:3826)
    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:2110)

以下是我扩展了FragmentActivity的ViewPagerActivity代码:
public class ViewPagerActivity extends FragmentActivity
{
    private ViewPager mViewPager;
    private ViewPagerAdapter adapter;
    boolean flag = false;

    @Override
    protected void onCreate(Bundle arg0)
    {
        super.onCreate(arg0);
        setContentView(R.layout.view_pager);
    }

    @Override
    protected void onStart()
    {
        super.onStart();
        setUpView();
    }

    private void setUpView()
    {
        mViewPager = (ViewPager) findViewById(R.id.viewPager);
        adapter = new ViewPagerAdapter(getApplicationContext(),getSupportFragmentManager());
        mViewPager.setAdapter(adapter);
        mViewPager.setCurrentItem(0);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item)
    {
        switch (item.getItemId())
        {
            case R.id.settings:
                startActivity(new Intent(this, AppSettingsActivity.class));
                return true;
            case R.id.addSituationMenu:
                Intent i = new Intent(this, MainLayout.class);
                i.putExtra("parentActivity", "SplashScreenLayout");
                startActivity(i);
                return true;
            case R.id.historyActivity:
                startActivity(new Intent(this, HistoryActivity.class));
                return true;
            case R.id.chartActivity:
                startActivity(new Intent(this, ViewPagerActivity.class));
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }

}

这行代码 mViewPager.setCurrentItem(0); 导致了崩溃。

这是 ViewPagerAdapter 的代码:

public class ViewPagerAdapter extends FragmentStatePagerAdapter
{
    private final Context context;
    ArrayList<ArrayList<Object>> data;
    int totalMoodEntries = 0;
    static Fragment f = null;

    public ViewPagerAdapter(Context mcontext, FragmentManager fm)
    {
        super(fm);
        context = mcontext;
    }

    @Override
    public Fragment getItem(int position)
    {
        switch (position)
        {
            case 0:
            {
                f = new ChartFragment(context, totalMoodEntries, data);
                break;
            }
            case 1:
            {
                f = new ViewRecordsFragment(context, data);
                break;
            }
            case 2:
            {
                f = new LearnMoreFragment(context);
                break;
            }
        }
        return f;
    }

    @Override
    public int getCount()
    {
        return 3;
    }
}
4个回答

2

您可以尝试将以下行移动到onCreate()方法中:

mViewPager = (ViewPager) findViewById(R.id.viewPager);
adapter = new ViewPagerAdapter(getApplicationContext(),getSupportFragmentManager());
mViewPager.setAdapter(adapter);

1

应用程序崩溃是由于在调用 ViewPagersetCurrentItem(0) 方法时,它会尝试销毁不可见的片段以释放内存。但此时这些碎片并不在活动生命周期中(因此出现 “无效索引2,大小为0”)。

它将保持的片段数量是通过方法 setOffscreenPageLimit() 指定的,默认值为1,这意味着最多会在内存中保留3个片段(正在显示的一个,前一个和后一个)。

片段的创建和销毁过程由 ViewPager 自动管理,您应该让它自己处理。此外,您正在使用 FragmentStatePagerAdapter,当它们被销毁时,它已经保存了不同片段的状态,但也使在片段之间传递信息变得更加复杂,因为它们不一定存在。

如果确实需要将第一个片段编程为可见片段,请尝试在 onResume() 中而不是 onStart() 中调用 setCurrentItem(),看看片段是否已经存在。


我所说的“做它的事情”是指你不应该将片段存储在内存中的集合中。这可能会导致可能的内存泄漏,尤其是使用FragmentStatePagerAdapter时更为严重,因为它的目标是仅在片段不可见时保留它们的状态以节省内存。 - Jorge

0
在我的情况下,问题有点不同。我认为抛出异常的类型相同,是由于我的PagerAdapter的instantiateItem()方法在我替换了当前的Fragment后,每当我按下“返回”按钮返回到带有ViewPager的原始Fragment时都会被调用。 抛出异常的确切行是:
((ViewPager) viewPagerContainer).addView(page, position);

在:

private class SmartViewPagerAdapter extends PagerAdapter {

    // [...]

    @Override
    public int getCount() {

        return ABOUT_VIEW_PAGER_IMAGES.length;
    }

    @Override
    public boolean isViewFromObject(View arg0, Object arg1) {

        return arg0 == (View) arg1;
    }

    @Override
    public Object instantiateItem(ViewGroup viewPagerContainer, int position) {

        Drawable pageImage = getResources().getDrawable(ABOUT_VIEW_PAGER_IMAGES[position]);
        Drawable pageTitle = getResources().getDrawable(ABOUT_VIEW_PAGER_TITLES[position]);
        String pageDescription = getString(ABOUT_VIEW_PAGER_DESCRIPTIONS[position]);

        SmartViewPagerPage page =
                new SmartViewPagerPage(getActivity(), pageImage, pageTitle, pageDescription);
        ((ViewPager) viewPagerContainer).addView(page, position);

        return page;
    }
}

事实证明,当您将View添加到ViewPager时,不应使用instantiateItem()方法的int参数,而只需使用0。 因此,该行应如下所示:
((ViewPager) viewPagerContainer).addView(page, 0);

希望这能帮到某个人 :)


0
问题在于你在创建新的ViewPageAdapter并将其设置为ViewPager后,设置了当前项0。你的片段仍未创建,因此返回一个越界异常。
在getItem方法上放置一个日志,并在setCurrentItem之前放置一个日志,并检查getItem是否在setCurrent之前调用(您的getItem正在创建片段)。
我不确定,但我认为强制setCurrentItem是不必要的,另一件事是您在每个getItem调用中都创建了一个新的片段。将它们保存在集合中以避免内存泄漏。

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