I'm implementing ViewPager+FragmentPagerAdapter with dynamic number of fragments. ViewPager itself works quite good. I'm able to add fragments to both sides. My ViewPager load some fragments at startup and new ones can be added "when needed" and data are available.

When loading begins? Imagine this as page numbers in viewpager:

0 1 2 3 4 5 6 7 8
_____       _____

When we reach one of underlined pages, we load new pages at side. Closer to the edge = more pages are loaded.

What really bothers me are little lags of SlidingTabLayout when new fragments are added and there is scroll in progress. You can see it at this video (youtube underestimate problem a little bit).

I use little bit modified SlidingTabLayout. I have just added method to reload child:

public void notifyDataSetChanged() {
    final PagerAdapter adapter = mViewPager.getAdapter();
    final OnClickListener tabClickListener = new TabClickListener();

    int childViewsCount = mTabStrip.getChildCount();
    int adapterChildCount = adapter.getCount();

    // get correct views count
    if(childViewsCount < adapterChildCount) { // need to add new views
        int missingViews = adapterChildCount - childViewsCount;
        for (int i = 0; i < missingViews; i++) {
            View tabView = null;
            if (mTabViewLayoutId != 0) {
                // If there is a custom tab view layout id set, try and inflate it
                tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
                        false);
            }

            if (tabView == null) {
                tabView = createDefaultTabView(getContext());
            }
            tabView.setOnClickListener(tabClickListener);
            mTabStrip.addView(tabView);
        }
    } else if(childViewsCount > adapterChildCount) { // need to delete some views
        int uselessViews = childViewsCount - adapterChildCount;
        for(int i = 0; i < uselessViews; i++) {
            mTabStrip.removeViewAt(0);
        }
    }

    for(int i = 0; i < adapterChildCount; i++) { // fill views with data
        View tabView = mTabStrip.getChildAt(i);

        TextView tabTitleView = null;

        if (mTabViewLayoutId != 0) {
            // If there is a custom tab view layout id set, try and inflate it
            tabView = LayoutInflater.from(getContext()).inflate(mTabViewLayoutId, mTabStrip,
                    false);
            tabTitleView = (TextView) tabView.findViewById(mTabViewTextViewId);
        }

        if (tabTitleView == null && TextView.class.isInstance(tabView)) {
            tabTitleView = (TextView) tabView;
        }

        tabTitleView.setText(adapter.getPageTitle(i));
    }

    getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

        @Override
        public void onGlobalLayout() {

            if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
                getViewTreeObserver().removeGlobalOnLayoutListener(this);
            } else {
                getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }

            int currentPosition = mViewPager.getCurrentItem();
            smoothScrollToTab(currentPosition, 0);
        }
    });
}

OnPageChangeListener implementation. Place where new fragments are added:

 ViewPager.SimpleOnPageChangeListener pageL = new ViewPager.SimpleOnPageChangeListener()  {        

    private int mState = -1;
    boolean pageSelected = false;

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        // figure out if we really scrolled to *new* position
        if (pageSelected &&
                (mState == ViewPager.SCROLL_STATE_SETTLING || mState == ViewPager.SCROLL_STATE_IDLE)) {

            currentPage = position;
            pageSelected = false;

            int addedFragmentsCount = 0;
            CalendarPA adapter = (CalendarPA) mPager.getAdapter();

            if(currentPage >= 0 && currentPage < PAGE_LIMIT_TO_LOAD) {
                int limit = PAGES_TO_LOAD - currentPage; // number of pages loaded depends on page we're at
                for(int i = 0; i < limit; i++) {
                    addedFragmentsCount += adapter.addFragmentLeft(defaultDataPos + (defaultPage + currentPage) +
                            (currentPage == 0 ? 1 : 0)); // currentPage == 0 -> we must not look for data at pos + 1
                }
                adapter.moveToRight(addedFragmentsCount); // rotate right
            }

            int offset = mPager.getAdapter().getCount() - currentPage;

            if(offset > 0 && offset < PAGE_LIMIT_TO_LOAD) {
                int limit = PAGES_TO_LOAD - offset; // number of pages loaded depends on page we're at
                for(int i = 0; i < limit; i++) {
                    adapter.addFragmentRight(defaultPage - currentPage + defaultDataPos - offset - i);
                }
            }
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        mState = state;
    }

    @Override
    public void onPageSelected(int position) {
        currentPage = position;
        pageSelected = true; // page has been selected
    }
};

FragmentPagerAdapter:

public class CalendarPA extends FragmentStatePagerAdapter {

    ArrayList<DummyFragment> mPagerFragments;
    ViewPager mPager;

    public CalendarPA(FragmentManager fm, ViewPager viewPager) {
        super(fm);
        mPager = viewPager;
        mPagerFragments = new ArrayList<DummyFragment>(); // first - pos 0 fragment
        mPagerFragments.add(new DummyFragment());
    }

    @Override
    public Fragment getItem(int pos) {
        DummyFragment df = mPagerFragments.get(pos);
        df.setData(data.getMyString(positionToDataPosition(pos)));
        return df;
    }

    @Override
    public int getCount() {
        return mPagerFragments.size();
    }

    public void moveToRight(int offset) {
        mPager.setCurrentItem(mPager.getCurrentItem() + offset, false);
    }

    @Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
        mSlidingTabLayout.notifyDataSetChanged();
    }

    public int addFragmentLeft(int dataPos) {
        String text = data.getMyString(dataPos);
        if(text == null) return 0; // no data

        DummyFragment df = new DummyFragment();
        df.setData(text);

        mPagerFragments.add(0, df);
        defaultPage++;
        notifyDataSetChanged();

        return 1; // fragment added
    }

    public int addFragmentRight(int dataPos) {
        String text = data.getMyString(dataPos);
        if(text == null) return 0; // no data

        DummyFragment df = new DummyFragment();
        df.setData(text);

        mPagerFragments.add(mPagerFragments.size(), df);
        notifyDataSetChanged();

        return 1; // fragment added
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }

    /**
     * Method to translate viewpager page position to data position
     * @param position page
     * @return return data position
     */
    public int positionToDataPosition(int position) {
        int temp = defaultPage - position;
        return defaultDataPos + temp;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return "POS :::::" + String.valueOf(positionToDataPosition(position));
    }
}

Rest of implementation on github. Can somebody has advice how to properly update SlidingTabLayout content without smooth transition? Thanks in advance.

Related posts

Recent Viewed