Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android ViewPager throwing IndexOutOfBounds exception when setting current item/page

I have a ViewPager with three items. I am trying to set the ViewPager to view the page furthest to the right (which would be the 2nd element). This is returning an IndexOutOfBounds exception, though I know the index ought to be in bounds. Here is the exact stack:

02-22 12:22:50.256: E/AndroidRuntime(384): FATAL EXCEPTION: main
02-22 12:22:50.256: E/AndroidRuntime(384): java.lang.IndexOutOfBoundsException: index=1 count=0
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.addInArray(ViewGroup.java:2050)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.addViewInner(ViewGroup.java:1994)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.addViewInLayout(ViewGroup.java:1958)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.addViewInLayout(ViewGroup.java:1939)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.support.v4.view.ViewPager.addView(ViewPager.java:917)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.addView(ViewGroup.java:1828)
02-22 12:22:50.256: E/AndroidRuntime(384):  at us.tagverse.pagertest.MasterActivity$PAdapter.instantiateItem(MasterActivity.java:518)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.support.v4.view.PagerAdapter.instantiateItem(PagerAdapter.java:110)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.support.v4.view.ViewPager.addNewItem(ViewPager.java:649)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.support.v4.view.ViewPager.populate(ViewPager.java:783)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.support.v4.view.ViewPager.onMeasure(ViewPager.java:1016)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.View.measure(View.java:8313)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.widget.LinearLayout.measureChildBeforeLayout(LinearLayout.java:1017)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.widget.LinearLayout.measureVertical(LinearLayout.java:386)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.widget.LinearLayout.onMeasure(LinearLayout.java:309)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.View.measure(View.java:8313)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.widget.FrameLayout.onMeasure(FrameLayout.java:250)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.View.measure(View.java:8313)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewGroup.measureChildWithMargins(ViewGroup.java:3138)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.widget.FrameLayout.onMeasure(FrameLayout.java:250)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.View.measure(View.java:8313)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewRoot.performTraversals(ViewRoot.java:839)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.view.ViewRoot.handleMessage(ViewRoot.java:1859)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.os.Handler.dispatchMessage(Handler.java:99)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.os.Looper.loop(Looper.java:123)
02-22 12:22:50.256: E/AndroidRuntime(384):  at android.app.ActivityThread.main(ActivityThread.java:3683)
02-22 12:22:50.256: E/AndroidRuntime(384):  at java.lang.reflect.Method.invokeNative(Native Method)
02-22 12:22:50.256: E/AndroidRuntime(384):  at java.lang.reflect.Method.invoke(Method.java:507)
02-22 12:22:50.256: E/AndroidRuntime(384):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
02-22 12:22:50.256: E/AndroidRuntime(384):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
02-22 12:22:50.256: E/AndroidRuntime(384):  at dalvik.system.NativeStart.main(Native Method)

Note that the cause is java.lang.IndexOutOfBoundsException: index=1 count=0. This would imply that the count of the pages/items is 0 and the index I am requesting is 1. Neither of these things are true. Here is how I am calling my ViewPager and requesting the given page:

ViewPager pager = (ViewPager) findViewById(R.id.ma_viewcontainer);
PAdapter adapter = new PAdapter();
pager.setAdapter(adapter);
pager.setCurrentItem(2);

Note that this call does not give any error, and correctly sets the current item to the middle item (index 1):

ViewPager pager = (ViewPager) findViewById(R.id.ma_viewcontainer);
PAdapter adapter = new PAdapter();
pager.setAdapter(adapter);
pager.setCurrentItem(1);

I have a count of 3 items in the view pager. Here is my full PAdapter class, which extends the PagerAdapter class:

private class PAdapter extends PagerAdapter implements TitleProvider {

    private int COUNT = 3;

    private static final int SETTINGS_ACTIVITY = 0;
    private static final int MAIN_ACTIVITY = 1;
    private static final int FRIEND_LIST_ACTIVITY = 2;

    @Override
    public int getCount() {
        return COUNT;
    }

    @Override
    public Object instantiateItem(View collection, int position) {
        LinearLayout layout = new LinearLayout(getApplicationContext());
        LayoutInflater inflater = (LayoutInflater) getApplicationContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        switch(position) {
        case MAIN_ACTIVITY :
            layout = (LinearLayout) inflater.inflate(R.layout.main, null, false);
            initMainLayout(layout);
            break;
        case SETTINGS_ACTIVITY :
            layout = (LinearLayout) inflater.inflate(R.layout.settings, null, false);
            initSettingsLayout(layout);
            break;
        case FRIEND_LIST_ACTIVITY :
            layout = (LinearLayout) inflater.inflate(R.layout.friend_list, null, false);
            initFriendListLayout(layout);
            break;
        }
        ((ViewPager)collection).addView(layout, position);
        return layout;
    }

    @Override
    public void destroyItem(View collection, int position, Object view) {
        ((ViewPager)collection).removeView((LinearLayout)view);
    }

    @Override
    public boolean isViewFromObject(View v, Object o) {
        return v == (LinearLayout)o;
    }

    @Override
    public void finishUpdate(View arg0) {
        //no need
    }

    @Override
    public void restoreState(Parcelable arg0, ClassLoader arg1) {
        //no need
    }

    @Override
    public Parcelable saveState() {
        return null;
    }

    @Override
    public void startUpdate(View arg0) {
        //no need
    }

    @Override
    public String getTitle(int position) {
        Resources res = getApplicationContext().getResources();
        switch(position) {
        case MAIN_ACTIVITY :
            return res.getString(R.string.app_name);
        case SETTINGS_ACTIVITY :
            return res.getString(R.string.settings);
        case FRIEND_LIST_ACTIVITY :
            return res.getString(R.string.friend_list);
        default :
            return null;
        }
    }
}

The line ((ViewPager)collection).addView(layout, position); causes the crash.

As you can see, three items. Using setCurrentItem() with a parameter of 0 or 1 works just fine, but 2 causes this strange error. I've run out of ideas to solve it... unfortunately ViewPager resides in some uncharted waters of Android it seems. If anyone has some insight on how to solve this it would be much appreciated. Thanks!

EDIT: As per Shereef's suggestion, I tried logging the child count via collection.getChildCount() and got the following result:

02-22 15:20:42.274: E/children count(645): count: 0
02-22 15:20:42.454: E/children count(645): count: 1
02-22 15:20:42.594: E/children count(645): count: 2

This is interesting. What this tells me is that its creating an empty PagerAdapter and adding them one at a time. instantiateItem() is called 3 times for the three visible views (center, left, and right). So I added this block of code inside the instantiateItem() method:

if(((ViewPager)collection).getChildCount() ==  2) {
    ((ViewPager)collection).setCurrentItem(2);
}

So only if the count is established to be 2 will it set the page to 2 (this is a hacky solution which doesn't fully address the problem, but was worth a try). I got a similar error stack which pointed first to the line: ((ViewPager)collection).setCurrentItem(2);, then ((ViewPager)collection).addView(layout, position);.

Hope this helps give some insight.

like image 302
JMRboosties Avatar asked Feb 22 '12 21:02

JMRboosties


2 Answers

I dont think you can just do ((ViewPager)collection).addView(layout, position). It depends on your off screen page limit, which by default is 1, I believe. I assume you are only using 3 views, so you can do something like viewPager.setOffscreenPageLimit(3); in your `onCreate (or wherever you placed your call) to avoid each view being refreshed every time you leave it.

You will see in a few tutorials that ((ViewPager) collection).addView(view, 0); is used in instantiateItem(). I have worked quite a bit with ViewPagers in the past few weeks, and using 0 as the position -all- the time seems to work just fine.

like image 124
aimango Avatar answered Oct 19 '22 22:10

aimango


I see the crash log I see that it crashes in instantiate item. I don"t know which line exactly because I don't have your line numbers

But if i was a betting man, I would bet that:

  ((ViewPager)collection).addView(layout, position);

Crashes for trying to add view at position 1 while there are no pages in the collection Maybe check the collection.getchildcount() (maybe the method name is wrong) and insert at max(position,count)

i.e replace position with:

count > position ? position : count

EDIT:

((ViewPager)collection).addView(layout, ((ViewPager)collection).getChildCount() > position ? position : ((ViewPager)collection).getChildCount())

p.s. Posting from my phone sorry for the horrible formatting

Please notice

 02-22 12:22:50.256: E/AndroidRuntime(384): at us.tagverse.pagertest.MasterActivity$PAdapter.instantiat 

MasterActivity.java:518

The number at the end of the line i copied and find out which line is it then and only then you will know which line got the crash

EDIT: P.S. about your solution

I think your solution only sets the current item after the 3 pages were instantiated by instantiate item, but my line would make it work even if they did not get instantiated.

like image 32
Shereef Marzouk Avatar answered Oct 19 '22 23:10

Shereef Marzouk