Pagination is a common issue with for a lot of mobile apps that need to deal with lists of data. Most of the mobile apps are now starting to take up the “endless page” model, where scrolling automatically loads in new content. CWAC Endless Adapter makes it really easy to use this pattern in Android applications. There is a good amount of documentation and sample code to get you started on the Github page. Here’s a very simple extension that I have been using successfuly in most of the apps for supporting this.

Including the endless adapter can’t be easier, just two JAR files (CWAC-Endless-Adapter, CWAC-Adapter) to put in libs. Now, you would want to create a new adapter that extends the EndlessAdapter. This new adapter will serve as the adapter that feeds data to the ListView (or any other AdapterView). i.e. you will have something like this where you set up your list view: listView.setAdapter(new UsersEndlessAdapter(this, users, 0)); All the view logic should be in a separate adapter (e.g. UsersAdapter) that creates the views for each user etc. The endless adapter is just concerned with creating the loading view and loading new data. Here’s an example:

public class UsersEndlessAdapter extends EndlessAdapter {

    private int page = 0;
    private UserList usersList;
    private RotateAnimation rotate = null;
    private View pendingView = null;

    public UsersEndlessAdapter(Context context, List<User> users, int page) {
        // Call super with the adapter that creates views for AdapterView.
        super(new UsersAdapter(context, users)); 

        // Initialize filter parameters.
        this.page = page;

        // Create a rotate animation.
        rotate = new RotateAnimation(0f, 360f, Animation.RELATIVE_TO_SELF,
                0.5f, Animation.RELATIVE_TO_SELF,
                0.5f);
        rotate.setDuration(600);
        rotate.setRepeatMode(Animation.RESTART);
        rotate.setRepeatCount(Animation.INFINITE);

    }

    @Override
    protected View getPendingView(ViewGroup parent) {
        // This is called when the adapter needs to show a pending 
        // view to indicate that new data is being loaded.
        View row = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.list_item_Users_loading, null);
        pendingView = row.findViewById(R.id.throbber);
        startProgressAnimation();

        return row;
    }

    @Override
    protected boolean cacheInBackground() throws Exception {
        // Called on the background thread when the adapter needs to fetch new data. 
        // Load the results and return true if there are more records remaining after this load.
        
        // Increase page counter.
        ++page;

        // Load next page of users. TODO Call your custom loading method here.
        usersList = User.load(page);

        // TODO Check if there are more results. This will depend on your implementation. 
        if (usersList == null || usersList.getMeta().getPagination().getRemaining() == 0) {
            return false;
        } else {
            return true;
        }
    }

    @Override
    protected void appendCachedData() {
        // Append the cached data to wrapped adapter. 
        UsersAdapter wrappedAdapter = (UsersAdapter) getWrappedAdapter();
        wrappedAdapter.append(usersList.getUsers());
    }


    void startProgressAnimation() {
        if (pendingView != null) {
            pendingView.startAnimation(rotate);
        }
    }
}