I'd like to change the master-detail implementation of my Android phone app. Currently, users can select items from a ListView
, opening a new activity. To select a different activity, the user must return to the list. Instead of this pogo-sticking, I'd like the user to swipe left and right to page through the documents using a ViewPager
. There can be many documents, so I'd like to load at most 3 pages at a time - the current page, the previous, and the next. Paging back and forth should then add and remove pages left and right. I've created an adapter implementing FragmentStatePagerAdapter
that handles static content (e.g. TextViews) nicely. Also deleting pages seems to work OK (not included here). But when I add e.g. an EditText
content is copied over from one page to the next when paging.
Below is the code for the adapter and for the activity. There are two questions I have:
EditText
from one fragment to the next?Pager Adapter:
public class DetailPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragments;
private final static String TAG = "DetailPagerAdapter";
public DetailPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
public void addItem(Fragment fragment) {
mFragments.add(fragment);
notifyDataSetChanged();
}
public void removeItem(int position) {
mFragments.remove(position);
notifyDataSetChanged();
}
public void insertItem(int position, Fragment fragment) {
mFragments.add(position, fragment);
notifyDataSetChanged();
}
}
PagingActivity Base Class:
public abstract class PagingActivity
extends AppCompatActivity
implements ViewPager.OnPageChangeListener {
protected ViewPager mViewPager;
DetailPagerAdapter mViewPagerAdapter;
protected ArrayList<String> mAllItemIds;
private String mPreviousItemId;
private String mCurrentItemId;
private String mNextItemId;
private boolean mMuteOnPageSelected = false;
protected abstract Fragment getNewPageFragment(String id);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
List<Fragment> initialFragments = new ArrayList<>();
int currentItemIndex = mAllItemIds.indexOf(mCurrentItemId);
int pageSelection = 1;
// Add previous view.
if (currentItemIndex > 0) {
mPreviousItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) - 1);
initialFragments.add(getNewPageFragment(mPreviousItemId));
} else {
pageSelection = 0;
mPreviousItemId = null;
}
// Add current view.
initialFragments.add(getNewPageFragment(mCurrentItemId));
// Add next view.
if (currentItemIndex < mAllItemIds.size() - 1) {
mNextItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) + 1);
initialFragments.add(getNewPageFragment(mNextItemId));
} else {
mNextItemId = null;
}
mViewPagerAdapter = new DetailPagerAdapter(getSupportFragmentManager(), initialFragments);
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setCurrentItem(pageSelection);
mViewPager.addOnPageChangeListener(this);
}
@Override
public void onPageSelected(int position) {
if (!mMuteOnPageSelected) {
mCurrentItemId = ((PagingFragment) (mViewPagerAdapter.getItem(mViewPager.getCurrentItem()))).getItemId();
int currentItemIndex = mAllItemIds.indexOf(mCurrentItemId);
// Navigated to the right.
if (position == mViewPagerAdapter.getCount() - 1) {
// Add next if not already pointing at the last available item.
if (currentItemIndex < mAllItemIds.size() - 1) {
mNextItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) + 1);
mViewPagerAdapter.addItem(getNewPageFragment(mNextItemId));
} else {
mNextItemId = null;
}
// If it succeeds remove first item.
int itemCount = mViewPagerAdapter.getCount();
if ((itemCount > 3) || ((itemCount == 3) && (currentItemIndex == mAllItemIds.size() - 1))) {
mMuteOnPageSelected = true;
mViewPagerAdapter.removeItem(0);
mViewPager.setCurrentItem(1);
mMuteOnPageSelected = false;
}
}
// Navigated to the left.
else if (position == 0) {
// Add item on the left if not already pointing at the first available item.
if (currentItemIndex > 0) {
mPreviousItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) - 1);
mViewPagerAdapter.insertItem(0, getNewPageFragment(mPreviousItemId));
} else {
mPreviousItemId = null;
}
// Check if last item needs to be removed and selection updated.
int itemCount = mViewPagerAdapter.getCount();
if (itemCount == 3) {
if (currentItemIndex == 0) {
// Points to the first of two items.
// -> do not change selection
// -> remove rightmost item.
mViewPagerAdapter.removeItem(itemCount - 1);
} else if (currentItemIndex == mAllItemIds.size() - 2) {
// Will point to the middle of 3 items.
// -> nothing to remove
// -> select middle page.
mMuteOnPageSelected = true;
mViewPager.setCurrentItem(1);
mMuteOnPageSelected = false;
}
} else if (itemCount > 3) {
// Pager contains 4 items, first item selected.
// -> remove rightmost item
// -> select middle page.
mMuteOnPageSelected = true;
mViewPagerAdapter.removeItem(itemCount - 1);
mViewPager.setCurrentItem(1);
mMuteOnPageSelected = false;
}
}
mViewPagerAdapter.notifyDataSetChanged();
}
}
}
The second question was the key: Yes, at least the current state can be achieved much easier by letting the adapter handle the full array of items. FragmentStatePagerAdapter
only loads as many fragments at a time as needed, so it can handle all the manual work I had done in the activity.
Pager Adapter
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private List<String> mAllItemIds;
public MyPagerAdapter(Context context, FragmentManager fm) {
super(fm);
mAllItemIds = ...
}
@Override
public int getCount() {
return mAllItemIds.size();
}
@Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
@Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mAllItemIds.get(position));
}
public void removeItem(int position) {
// add needed code here to remove item also from source
// ...
mAllItemIds.remove(position);
notifyDataSetChanged();
}
}
Activity
public abstract class PagingActivity extends AppCompatActivity {
protected ViewPager mViewPager;
MyPagerAdapter mViewPagerAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = (ViewPager)findViewById(R.id.viewPager);
mViewPagerAdapter = new MyPagerAdapter(this, getSupportFragmentManager());
mViewPager.setAdapter(mViewPagerAdapter);
}
private void deleteItem() {
mViewPagerAdapter.removeItem(mViewPager.getCurrentItem());
}
}