简体   繁体   中英

Android ViewPager Exception after pressing back button

I have a ViewPager which contains 3 fragments which is working fine. I start an activity from my any of my fragments inside the viewpager and the activity is displayed. After that when i press the back button, my application crashes with the following excepion:

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)

The following is my code for ViewPagerActivity that extends FragmentActivity::

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

}

The line mViewPager.setCurrentItem(0); causes the crash.

This is the ViewPagerAdapter code:

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

Can you try by moving following line in the onCreate() method:

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

The application is crashing because when you call the setCurrentItem(0) method of the ViewPager it tries to destroy the non-visible fragments to release memory. But at that point of the activity lifecycle, they aren't still there (hence the Invalid index 2, size is 0 ).

The number of fragments that it will keep alive is specified through the method setOffscreenPageLimit() , 1 by default, which means that at most 3 fragments will be retained in memory (the one it´s being shown, the previous one and the next one).

The process of creation and destruction of fragments is managed automatically by the ViewPager , and you should let it do its stuff. Besides, you are using a FragmentStatePagerAdapter , which already saves the states of the different fragments when they are destroyed, but it also makes it a little bit more complex passing information between fragments, because they don´t necessarily exist.

If you really need to programatically set the first fragment as the visible fragment, simply try calling the setCurrentItem() in onResume() instead of onStart() , and see if the fragments are already there.

The problem is that you are setting current item 0, after creating a new ViewPageAdapter and setting it to the ViewPager. Your fragments are still not created so it return a out of bounds exception.

Put a log on the getItem method, and a log before setCurrentItem, and check that your getItem is not called before the setCurrent (Your getItem is creating the fragments).

I'm not sure, but in my opinion it's not necesary to force a setCurrentItem, and the another thing is that you are creating a new fragment in each getItem call. Save them on a collection to avoid memory leaks

In my case the problem was a bit different I think. I had the same type of exception thrown by the instantiateItem() method of my PagerAdapter whenever I replaced the current Fragment I was in and then pressed the Back button to go back to the original one with the ViewPager . The exact line throwing the exception was:

((ViewPager) viewPagerContainer).addView(page, position);

in:

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

As it turned out, when you add a View to your ViewPager , you should not use the int argument of the instantiateItem() method but just use 0. Thus, the line should look like this:

((ViewPager) viewPagerContainer).addView(page, 0);

I hope this helps someone :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM