Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tell Android AsyncTaskLoader to retrieve more data

Let me start off by saying this is NOT a question about scrolling ListViews. I do not want to know how to tell when a user scrolls to the bottom of a list, so please do not give me answers for that question, or mark this as a duplicate.

I am using a class that extends AsyncTaskLoader to populate a ListView with data from a web service.

Initially, I load 50 items and everything is working great. I need to know how to tell the Loader to load the next 50 items incrementally. I understand WHERE to do this in the ListView code, but I can't figure out the best way to tell the Loader that I want to load more data without resetting it and loading everything again.

Again, to clarify, the issue I'm trying to solve here is just notifying the loader that more data needs to be loaded. It already knows how to load more data when loadInBackground() is called a second time, and the ListView already knows where/when to notify the Loader, the question is just how.

Some of the relevant code:

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);

    m_adapter = new SearchAdapter(getActivity());
    setListAdapter(m_adapter);

    // if the loader doesn't already exist, one will be created
    // otherwise the existing loader is reused so we don't have
    // to worry about orientation and other configuration changes
    getLoaderManager().initLoader(SEARCH_LOADER_ID, null, this);
}

@Override
public Loader<List<Result>> onCreateLoader(int id, Bundle args)
{
    String query = args != null ? args.getString(QUERY_KEY) : "";
    return new SearchLoader(getActivity(), query);
}

private class SearchAdapter extends ArrayAdapter<Result>
{
    // ...

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {

        // ...

        if (position == getCount() - 2)
            // TODO: Need to notify Loader here

        // ...
    }
}

private static class SearchLoader extends OurAsyncTaskLoader<List<Result>>
{
    public SearchLoader(Context context, String query)
    {
        super(context);

        m_query = query;
        m_data = Lists.newArrayList();
        m_loadedAllResults = false;
    }

    @Override
    public List<Result> loadInBackground()
    {
        if (m_loadedAllResults)
            return m_data;

        // the Loader implementation does a == check rather than a .equals() check
        // on the data, so we need this to be a new List so that it will know we have
        // new data
        m_data = Lists.newArrayList(m_data);

        MyWebService service = new MyWebService();
        List<Result> results = service.getResults(m_query, m_data.size(), COUNT);
        service.close();

        if (results == null)
            return null;

        if (results.size() < COUNT)
            m_loadedAllResults = true;

        for (Result result : results)
            m_data.add(result)

        return m_data;
    }

    private static final int COUNT = 50;

    private final String m_query;

    private boolean m_loadedAllResults;
    private List<Result> m_data;
}
like image 439
drewhannay Avatar asked Jun 03 '13 14:06

drewhannay


1 Answers

I figured out a way that works. In my SearchAdapter#getView() method, I have the following code:

private class SearchAdapter extends ArrayAdapter<Result>
{
    // ...

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {

        // ...

        if (position == getCount() - 2)
            getLoaderManager().getLoader(SEARCH_LOADER_ID).onContentChanged();
        // ...
    }
}

I still would like to know if this is the "best practice" way of doing it, but it seems to solve my problem for now.

like image 115
drewhannay Avatar answered Oct 30 '22 11:10

drewhannay