PagerAdapter class getting called multiple times
Asked Answered
S

5

10

I am working on an application which has a ViewPager view in it, I have created a PagerAdapter, which has the view, instantiateItem() method of PagerAdapter is called twice in create() i don't know why, can anyone help me with this?

Here is my code,

         View PagerView;
        MyPagerAdapter adapter;
        ViewPager pager;

            adapter = new MyPagerAdapter();     
    pager.setAdapter(adapter);
    pager.setCurrentItem(0);

public class MyPagerAdapter extends PagerAdapter {

        @Override
        public Object instantiateItem(final View collection, final int position) {
            Log.d("Inside", "Pager");
            PagerView = new View(collection.getContext());
            LayoutInflater inflater = (LayoutInflater) collection.getContext()
                    .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            PagerView = inflater.inflate(R.layout.tablemenu, null, false);
            tbMenuDetails = (TableLayout) PagerView
                    .findViewById(R.id.Menutable1);
            scrollview = (ScrollView) PagerView.findViewById(R.id.scrollView1);
            tbMenuDetails.removeAllViews();
            removeTableRows();
            createTableLayout(position);
            String str[][] = datasource.GetSubMenuDetailsFromMenuId(MenuIdlst
                    .get(position).trim());
            Log.d("Str", "" + str.length);
            for (int i = 0; i < str.length; i++) {
                addRows(str[i][1], str[i][2], str[i][0], str[i][3], position);
                Log.d("Message", "Pos   " + position + "    SubMenuName" + str[i][2]
                        + " SubMenuId" + " " + str[i][0] + " TypeID" + "    "
                        + str[i][3]);
            }

            // View view = inflater.inflate(resId, null);
            ((ViewPager) collection).addView(PagerView, 0);

            return PagerView;
        }

        @Override
        public void destroyItem(final View arg0, final int arg1,
                final Object arg2) {
            ((ViewPager) arg0).removeView((View) arg2);

        }

        @Override
        public boolean isViewFromObject(final View arg0, final Object arg1) {
            return arg0 == ((View) arg1);

        }

        @Override
        public void finishUpdate(View arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {
            // TODO Auto-generated method stub

        }

        @Override
        public Parcelable saveState() {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        public void startUpdate(View arg0) {
            // TODO Auto-generated method stub

        }

        @Override
        public int getCount() {
            // TODO Auto-generated method stub
            return MenuIdlst.size();
        }

    }

Please help

Sympathizer answered 15/2, 2013 at 10:46 Comment(13)
No one can help me out??Sympathizer
how are you instantiating the pager with the fragments? paste that code. (onCreate() code)Too
adapter = new MyPagerAdapter(); pager.setAdapter(adapter); pager.setCurrentItem(0); pager.setOffscreenPageLimit(0);Sympathizer
and are you using fragments in the pager?Too
done but its still called twiceSympathizer
Offscreen page limit cannot be set to zero. See my answer for details.Shackleford
Can you please tell why you need this in your app or why the twice calling of instantiateItem creates problem??Tobolsk
@Shackleford you can modify the source code of ViewPager to override the minimum one offset behaviour . Check this dropbox link for my modified viewpager Link...Tobolsk
@Tobolsk You get precisely the effect I said in my answer - you can't see the next page when you scroll.Shackleford
@Shackleford sorry but i have implemented it using zero offscreen and it work perfectly except that the animation doesn't happen when u scrollTobolsk
@Tobolsk If you scroll between the first and second page slowly, you should be able to see part of the next page. However, if you set offscreen page limit to zero, you will not see anything for the next page, until it is fully scrolled. Switch the limit between 0 and 1 and you can see the difference, when scrolling slowly. If this is what you mean by 'animation doesn't happen', then we are talking about the same thing. Partial scrolling doesn't draw the next page. I gave an explanation in the answer - the second page needs to be loaded for us to see anything.Shackleford
@Shackleford yes we both are talking about the same thingTobolsk
a straightforward option would be to actually check the position before you do anythingCreeper
E
6

If you decide to use Fragments, don't implement PagerAdapter. Instead, extend FragmentPagerAdapter or FragmentStatePagerAdapter:

private class MyPagerAdapter extends FragmentStatePagerAdapter {
    public MyPagerAdapter(FragmentManager fm) {
        super(fm);
    }

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

    @Override
    public Fragment getItem(int position) {
        // Implement the static method newInstance in MyFragment.java yourself.
        // It should return you a brand new instance of MyFragment, basically using
        // the code you had in your original instantiateItem method.
        return MyFragment.newInstance(position, ... etc ...); 
    }
}

Then in your Activity:

myPagerAdapter = new MyPagerAdapter(getFragmentManager());
// or myPagerAdapter = new MyPagerAdapter(getSupportFragmentManager());
myViewPager.setAdapter(myPagerAdapter);

To get more info on how to use Fragments and ViewPagers: https://developer.android.com/reference/android/app/Fragment.html https://developer.android.com/reference/android/support/v4/view/ViewPager.html https://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html

Erewhile answered 11/3, 2013 at 22:25 Comment(5)
-Streets Of Boston Where should i initialize the views which are inside the ViewPager?Sympathizer
The getItem(int pos) returns the Fragment you want to show on page 'pos'. The fragment (MyFragment) is a subclass of Fragment. You create the Views in this subclass' implementation of onCreateView: developer.android.com/reference/android/app/…, android.view.ViewGroup, android.os.Bundle)Erewhile
I have used same and give same results.. called twice on preloadingWatchword
@StreetsOfBoston Thanks, i have same problem. could you review this code? please paste.debian.net/786168Arapaima
I had the similar kind of problem while using ViewPager2 and found out that viewPager.setOffscreenPageLimit(1); was causing the problem. In the official documentation it says: "Value to indicate that the default caching mechanism of RecyclerView should be used instead of explicitly prefetch and retain pages to either side of the current page." for public static final int OFFSCREEN_PAGE_LIMIT_DEFAULT = 0; I rely on the recyclerview default caching mechanism instead of explicitly setting viewpager's offscreen page limit.Demonography
N
4

ViewPager by default preloads one page ahead / before the current page (if any). You didn't say if it's being called for the same or different position.

Nealah answered 15/2, 2013 at 11:8 Comment(3)
If you do not want the preloading to happen, you can call setOffscreenPageLimit(0) on your ViewPager. developer.android.com/reference/android/support/v4/view/…Nealah
Its Still getting called twiceSympathizer
Offscreen page limit cannot be set to zero. See my answer for details.Shackleford
S
3

ViewPager.setOffscreenpageLimit(int) has a minimum value of 1. You can see this from the source code for ViewPager:

private static final int DEFAULT_OFFSCREEN_PAGES = 1;

...

/**
     * Set the number of pages that should be retained to either side of the
     * current page in the view hierarchy in an idle state. Pages beyond this
     * limit will be recreated from the adapter when needed.
     *
     * <p>This is offered as an optimization. If you know in advance the number
     * of pages you will need to support or have lazy-loading mechanisms in place
     * on your pages, tweaking this setting can have benefits in perceived smoothness
     * of paging animations and interaction. If you have a small number of pages (3-4)
     * that you can keep active all at once, less time will be spent in layout for
     * newly created view subtrees as the user pages back and forth.</p>
     *
     * <p>You should keep this limit low, especially if your pages have complex layouts.
     * This setting defaults to 1.</p>
     *
     * @param limit How many pages will be kept offscreen in an idle state.
     */
    public void setOffscreenPageLimit(int limit) {
        if (limit < DEFAULT_OFFSCREEN_PAGES) {
            Log.w(TAG, "Requested offscreen page limit " + limit + " too small; defaulting to " +
                    DEFAULT_OFFSCREEN_PAGES);
            limit = DEFAULT_OFFSCREEN_PAGES;
        }
        if (limit != mOffscreenPageLimit) {
            mOffscreenPageLimit = limit;
            populate();
        }
    }

You should see a warning in Logcat if you try to set it to zero.

The lower limit is 1 for a very good reason. The adjacent page needs to be already loaded when you scroll the pager - otherwise you will not see anything for the next page. If you manage to force the offscreen page limit to zero, you would probably just see a black, empty page as you scroll from the first page to the second. If you have a particular problem with both the first and second pages being created at the beginning, then try to target and fix that.

Shackleford answered 13/3, 2013 at 6:34 Comment(1)
"try to target". What does that mean ? (9 years later)Addict
T
2

Use fragments for each view in a pager.

write the below code in onCreate() method of the FragmentActivity.

List<Fragment> fragments = new Vector<Fragment>();

//for each fragment you want to add to the pager
Bundle page = new Bundle();
page.putString("url", url);
fragments.add(Fragment.instantiate(this,MyFragment.class.getName(),page));

//after adding all the fragments write the below lines

this.mPagerAdapter  = new PagerAdapter(super.getSupportFragmentManager(), fragments);

mPager.setAdapter(this.mPagerAdapter);

A sample fragment definition:

public class MyFragment extends Fragment {


public static MyFragment newInstance(String imageUrl) {

final MyFragment mf = new MyFragment ();

    final Bundle args = new Bundle();
    args.putString("somedata", "somedata");
    mf.setArguments(args);

    return mf;
}

public MyFragment() {}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    String data = getArguments().getString("somedata");
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // Inflate and locate the main ImageView
    final View v = inflater.inflate(R.layout.my_fragment_view, container, false);
    //... 
    return v;
}

I follow this method whenever i need to use ViewPager. Hope this helps. I couldn't figure out why your instantiate method was being called twice from the information you have provided.

Too answered 18/2, 2013 at 6:30 Comment(1)
NO i am Not getting how to implement this is my app, can you suggest me the changes in my codeSympathizer
S
-1

There change to make that is essential for the isViewFromObject() method. It is very important and in it's documentation it is said that "This method is required for a PagerAdapter to function properly."

@Override
public boolean isViewFromObject(View view, Object object) {
    if(object != null){
        return ((Fragment)object).getView() == view;
    }else{
        return false;
    }
}

You can look here.

Stopple answered 19/11, 2014 at 13:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.