Android : ViewPager with dynamic load of data

on Thursday, August 14, 2014


We have an activity that displays data in a ViewPager.


This data has been pre-fetched from a remote database and is held in a separate singleton class.


That class supplies the following actions:



  1. Fetch and store initial data

  2. Fetch more data and add to the current list of data

  3. An Interface that allow the "caller" to know when data has been fetched.


The ViewPager then accesses this data and displays it.


What I am trying to do is to pre-load a certain number of entries (50) and display them. Then, when the used attempts to page to entry 51 I want to go and fetch more data.


So far, I have managed to get it to call the fetch code when needed but I can't seem to be able to get the pages to refresh. The ViewPager doesn't seem to know that more data is available. I have tried to do this by catching the scroll in onPageScrollStateChanged and, if it is the last entry, I get more data. The extra data is fetched in an async thread but a timer is shown that prevents user interaction while the data is being fetched.


I've tried removing and re-attaching the adapter, tried using notifyDataSetChanged() and tried returning POSITION_NONE in getItemPosition.


Sometimes it fails to update the screen at all whilst at other times I get an exception: "Fragment is not currently in FragmentManager".


This is my main class which, as can be seen, include two subclass - the adapter and the actual fragment.



public abstract class SwipeableDetailsScreen extends ContentScreen {


// ******** SwipeableFragment - Start ****************
protected abstract class SwipeableFragment extends Fragment{
/**
* Prints the details of the selected item to the screen
* @param contentView
*
* @param selectedItem An object containing data about the selected searchable from the ListView of SearchableListScreen
*
* Note. This method is NOT executed when the object is instantiated. It is executed when the screen is being prepared for display.
*/
public abstract void setSelectedItemDetails(ViewGroup contentView, Searchable selectedItem);


private Searchable _selectedItem;

public SwipeableFragment(Searchable selectedItem){
_selectedItem = selectedItem;
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.searchable_details_screen, container, false);

// Set the data for the selected Searchable in the appropriate TextViews of this screen:
this.setSelectedItemDetails(contentView, _selectedItem);

return root;
}


protected abstract int getLayoutId();
}

// ******** SwipeableFragment - End ****************



// ******** SwipeableFragmentAdapter - Start ****************

protected abstract class SwipeableFragmentAdapter extends android.support.v13.app.FragmentStatePagerAdapter implements IconPagerAdapter, OnPageChangeListener {

private ViewPager _pager;
private boolean _needToGetMoreData = false;

public SwipeableFragmentAdapter(FragmentManager fragmentManager, ViewPager pager) {
super(fragmentManager);
_pager = pager;
_pager.setOnPageChangeListener(this);
}

@Override
public Fragment getItem(int position){
Fragment fragment;
if (position == 0){
_needToGetMoreData = true;
fragment = getFragment(null);
}
else{
_needToGetMoreData = false;
fragment = getFragment((Searchable) items[items.length-position-1]);
}
return fragment;
};

public abstract Fragment getFragment(Searchable item);


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

@Override
public int getCount() {
return items.length;
}

protected abstract CharSequence getName(int position);

@Override
public final CharSequence getPageTitle(int position){
CharSequence title = getName(position);

int currentPosition = _pager.getCurrentItem();

if (position < currentPosition)
title = title + " > ";
else if (position > currentPosition)
title = " < " + title;

return title;


}

public void setCount(int count) {
}

@Override
public int getIconResId(int index) {
// TODO Auto-generated method stub
return 0;
}

@Override
public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_DRAGGING){
if (_needToGetMoreData){
ListResults.getInstance()
.addlistener(new ConnectionFinishedListener(){

@Override
public void connectionFinished(boolean error, boolean haveData, boolean lastChunk) {

// SwipeableFragmentAdapter.this.notifyDataSetChanged();
// SwipeableFragmentAdapter.this._pager.setAdapter(null);
SwipeableFragmentAdapter.this._pager.setAdapter(SwipeableFragmentAdapter.this);

}})
.getMoreData();
}
}

}

@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
// TODO Auto-generated method stub

}

@Override
public void onPageSelected(int arg0) {
// TODO Auto-generated method stub

}

}


// ******** SwipeableFragmentAdapter - End ****************



// ******** Outer Class Methods ************

@Override
public void onCreate(Bundle savedInstanceState) {
// The super.onCreate() call will set the general frame also for this Screen:
super.onCreate(savedInstanceState);

// Read the details from the calling (previous) Activity and determine the layout for this screen:
Bundle bundle = getIntent().getExtras();
int currentItem = bundle.getInt(ListScreen.SELECTED_ITEM_POSITION,0);

// Add the content of this specific screen to the ScrollView:
ViewPager pager = (ViewPager) stb.findViewById(R.id.pager);
pager.setAdapter(getPagerAdapter(pager));
pager.setCurrentItem(items.length - currentItem - 1);

}

protected abstract SwipeableFragmentAdapter getPagerAdapter(ViewPager pager);


}


This class and its subclasses are then extended in order to supply screen-specific data.


The important point is that I do not want to fetch data unless I have to. In other words, I cannot pre-fetch data when the user is "approaching" the last entry. I must fetch the data ONLY when the user is trying to see the entry after the last one already fetched.


0 comments:

Post a Comment