In my app I use a ViewPager in conjunction with PageTransformer to display some data list as a card deck. That list is sometimes refreshed and it contains plenty of items in a common case, so I use FragmentStatePagerAdapter with needed page limit:
public class PlaceListPagerAdapter extends FragmentStatePagerAdapter {
private List<PlaceListData> items;
public PlaceListPagerAdapter (FragmentManager manager) {
super (manager);
items = new ArrayList<>();
}
@Override
public Fragment getItem(int position) {
String id = items.get(position).getId();
String name = items.get(position).getName();
String address = items.get(position).getAddress();
return PlaceFragment.newInstance(id, name, address);
}
@Override
public int getCount() {
return items.size();
}
@Override
public int getItemPosition(Object object) {
PlaceFragment frag = (PlaceFragment) object;
String id = frag.getDataId();
for (PlaceListData data : items) {
if (id != null && id.equals(data.getId())) {
return POSITION_UNCHANGED;
}
}
return POSITION_NONE;
}
public void addItems(List<PlaceListData> items) {
this.items.addAll(items);
notifyDataSetChanged();
}
public void replaceItems(List<PlaceListData> items) {
this.items.clear();
addItems(items);
}
public void clearItems () {
this.items.clear();
notifyDataSetChanged();
}
}
Nothing unusual - list of pojos, overriden getItemPosition() and add/clear methods with notifyDataSetChanged(). The problem is when I try to add/replace items over network, method transformPage(View view, float position) of my transformer is called with 0.0 position for all new views and they are placed one over another (transformation is broken).
After debugging ViewPager class I noticed that transformPage(View view, float position) is invoked only in one place in onPageScrolled(..) method of ViewPager and the latter is invoked in onLayout(..) after the first layout pass.
notifyDataSetChanged() invokes onPageScrolled(..) (and transformPage(View view, float position) respectively) and requestLayout() after that. The problem is that first invocation of transformPage(View view, float position) happens before layout pass and position parameter depends on View.left() which returns 0 as child views are not layouted. In onLayout(..) which follows the requestLayout() my transformPage(View view, float position) is not called at all because yeap, views are layouted now, but it is not the first layout pass.
The problem is cured by resetting adapter (it sets mFirstLayout flag in ViewPager to "true") or by calling any public method of ViewPager which invokes transformPage(View view, float position) in it's code such as scrollTo, fakeDrag etc, but I'd like to know - is all that a feature of ViewPager or is it a bug or am I doing something very very wrong?
Sorry for my bad English and thanks in advance!