kha
kha

Reputation: 20013

Strange view pager behavior when swiping fast

I seem to be having a strange problem.

My ViewPager is working perfectly fine when I'm swiping it "slowly" (as in, there's a gap between my swipes - even half a second) but if I swipe fast enough (2-3 pages a second), after 3-4 pages, I observe one of these behaviors:

This is my adapter:

public class SectionsPagerAdapter extends FragmentStatePagerAdapter {

    private SparseArray<String> _fragmentHeaders = new SparseArray<String>();
    private SparseArray<Fragment> _fragments = new SparseArray<Fragment>();
    private Integer _previousCount = null;
    private boolean _isDatasetBeingChanged;

    public SectionsPagerAdapter(FragmentManager fm) {
        super(fm);

        registerDataSetObserver(new DataSetObserver() {
            @Override
            public void onChanged() {
                _previousCount = getContentCount();
                super.onChanged();
            }
        });
    }

    public Fragment getExistingItem(int position) {

        Fragment fragment = _fragments.get(position, null);
        if (fragment != null) {
            return fragment;
        }

        return null;
    }

    @Override
    public Fragment getItem(int position) {
        TType content = findContentWithPosition(position);
        if (_fragmentHeaders.get(position, null) == null) {
            _fragmentHeaders.put(position, content.getTitle());
        }

        Fragment fragment = createFragmentFromType(content);
        _fragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        RoboFragment fragment = (RoboFragment) object;
        if (fragment != null) {
            fragment.onDestroy();
        }

        if (_fragmentHeaders.get(position, null) != null) {
            _fragmentHeaders.remove(position);
        }

        if (_fragments.get(position, null) != null) {
            _fragments.remove(position);
        }

        super.destroyItem(container, position, object);
    }

    @Override
    public int getCount() {
        int count = getContentCount();
        if (_isDatasetBeingChanged) {
            return count;
        }

        if (_previousCount != null && _previousCount != count) {
            _isDatasetBeingChanged = true;
            notifyDataSetChanged();
        }

        _previousCount = count;
        _isDatasetBeingChanged = false;
        return _previousCount;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return _fragmentHeaders.get(position);
    }
}

which as I mentioned works if I swipe slowly. And I simply add the adapter like this:

_sectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
_viewPager.setAdapter(_sectionsPagerAdapter);

Has anyone seen this before? I'm using rev 22 of the support libraries and everything is up to date in terms of the SDK.

I've tested this behavior on both 5.0 and 4.4 versions and both exhibit this problem.

Many thanks in advance,

p.s. the problem is not with calling notifyDataSetChanged(). Removing it had no effects and this happens even if the underlying page count has not changed.

Update 1:

I came across this Google page that seems to describe the problem I've mentioned here:

http://code.google.com/p/android-developer-preview/issues/detail?id=1190

The only difference is, with or without the CardView I get the same error so I don't think the problem is limited to the card view.

No resolution to this problem though so if anyone can help, I will be more than grateful.

Upvotes: 4

Views: 3174

Answers (2)

kha
kha

Reputation: 20013

I found out the problem after many hours of debugging into the source code of ViewPager. The issue was the my fragment contained a view that was also listening to touch events. When scrolling on a normal speed, the two would not interfere with each other (there's a Runnable() in the ViewPager I recommend having a look at and to understand its interaction with scrolling and also to understand how the velocity and "page jumping in VP operates) but when I was scrolling fact, the combination of the runnable and the velocity in the VP's onTouch would not play nicely with my view.

The solution was to remove the inner view that was listening to onTouch. While this was an extreme measure to take and made change some rather significant parts of my code and layout, I was simply too afraid to copy the entire VP's source code and change the part that was causing problems.

Upvotes: 5

android_student
android_student

Reputation: 1246

in your getItem method, do you mean to create a new fragment each time? what is _fragments? Instead try :

@Override
public Fragment getItem(int position) {
    Fragment fragment = _fragments.get(position);
    if (fragment == null) {
        fragment = createFragmentFromType(content);
        _fragments.put(position, fragment);
        //save your fragment names, or better, create a custom fragment with a getTitle method.
    }

    return fragment;
}

for your getCount() method try:

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

get rid of your override for destroyItem(), seems overly complicated and unnecessary.

Upvotes: 0

Related Questions