What I'm trying to achieve:
An Activity with a ViewPager that displays Fragments for a list of Objects in the Adapter (FragmentStatePagerAdapter
).
Initially the Activity loads N (lets say 5) objects from the SQLite DB into the Adapter. These objects are chosen with some randomness.
When the user is reaching the end of the list, the activity shall load M (let M be 3) more objects from the DB, add them to the adapter and call notifyDataSetChanged()
. When adding them, I check if the new Objects already exist in the list and if they do, the pre-existing one gets removed and the loaded one gets added to the list's tail.
Thus, I'm trying to achieve something like an infinite scrolling ViewPager (NOT a "circular" ViewPager as I want new Objects to be fetched constantly, instead of going back to the begging of the list).
I have some working code and included a sample for the pattern I'm following down bellow. However, I keep getting this exception and have no idea why:
IllegalStateException: Fragment MyObjectFragment{id...} is not currently in the FragmentManager
at android.app.FragmentManagerImpl.saveFragmentInstanceState(FragmentManager.java:553)
at android.support.v13.app.FragmentStatePagerAdapter.destroyItem(FragmentStatePagerAdapter.java:140)
at android.support.v4.ViewPager.populate(ViewPager.java:1002)
...
Code Sample:
The Activity:
public class MyActivitty extends FragmentActivity {
public MyPagerAdapter adapter;
public ViewPager pager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_acyivity_layout);
pager = (ViewPager) findViewById(R.id.viewPager);
ArrayList<MyObject> myObjects = new ArrayList<MyObject>();
// loadInitialObjectsFromDB(int N) goes to SQLite DB and loads the N first objects to show on the ViewPager...
myObjects = loadInitialObjectsFromDB(5);
// Adapter will use the previously fetched objects
adapter = new MyPagerAdapter(this, getFragmentManager(), myObjects);
pager.setAdapter(adapter);
pager.setOffscreenPageLimit(2);
// (...)
}
// (...)
}
The PagerAdapter:
public class MyPagerAdapter extends FragmentStatePagerAdapter implements
ViewPager.OnPageChangeListener {
private MyActivity context;
private ArrayList<MyObject> objectList;
private int currentPosition = 0;
// (...)
public MyPagerAdapter(MyActivity context, FragmentManager fragmentManager, ArrayList<MyObject> objects)
{
super(fragmentManager);
this.context = context;
this.objectList = objects;
}
@Override
public Fragment getItem(int position)
{
MyObject object = objectList.get(position);
return MyObjectFragment.newInstance(position, object);
}
@Override
public int getItemPosition(Object object){
MyObjectFragment frag = (MyObjectFragment) object;
MyObject object = frag.getMyObject();
for(int i = 0; i < objectList.size(); i++)
{
if(objectList.get(i).getId() == object.getId())
{
return i;
}
}
return PagerAdapter.POSITION_NONE;
}
@Override
public int getCount()
{
return objectList.size();
}
@Override
public void onPageSelected(int position)
{
currentPosition = position;
}
// (...)
}
@Override
public void onPageScrollStateChanged(int state)
{
switch(state)
{
case ViewPager.SCROLL_STATE_DRAGGING:
// (...)
break;
case ViewPager.SCROLL_STATE_IDLE:
// if we are reaching the "end" of the list (while scrolling to the right), load more objects
if(currentPosition <= position && position >= answerList.size()-1)
{
// Function in MyActivity that fetches N more objects.
// and adds them to this adapter's ArrayList<MyObject> objectList
// it checks for duplicates so that if the fetched object was already in the back of the list, it is shown again
context.getMoreObjects(3);
notifyDataSetChanged();
}
getMoreQuestions(currentPosition);
case ViewPager.SCROLL_STATE_SETTLING:
break;
}
}
My Fragment:
public class MyObjectFragment extends Fragment {
// Object to represent
private MyObject object;
public static Fragment newInstance(MyActivity context,
int position, MyObject object) {
MyObjectFragment frag = new MyObjectFragment();
Bundle args = new Bundle();
args.putParcelable("Object", object);
frag.setArguments(args);
return frag;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
this.currentInflater = inflater;
final View rootView = inflater.inflate(
R.layout.fragment_my_object, container, false);
// get object from Bundle, set UI, events, etc...
}
// (...)
}
Any idea on why am I getting this Exception
? It seems like the FragmentStatePagerAdapter
is trying to destroy an item that no longer exists, but I don't understand why.
EDIT 1:
If I comment my @Override getItemPosition(Object object)
, I don't get the exception anymore. However, I need to override getItemPosition
because there is a use case in which the user deletes the currently shown Object causing it to disappear from the adapter's array and forcing the getItemPosition
to return POSITION_NONE
if the item doesn't exist anymore.
EDIT 2:
Now I do know that this exception only happens when I remove items from my adapter's objectList
. I have two situations where MyObject
instances are deleted from the objectList
:
When the
getMoreObjects()
adds fetches an object from the DB that was already in theobjectList
, I delete it and re-add it to the head of the list. I do this to avoid having objects with the same Id in the objectList, as their Id is used by thegetItemPosition()
to know if they exist and their position.Before returning,
getMoreObjects()
, removes the N first objects from the list. I do know that the FragmentStatePagerAdapter already saves memory by only keeping in memory fragments for some of the objects, but I still would like to avoid growing myobjectList
too much. For now, I have this line commented, as it's not that important.