1

I'm trying to make a LazyList EndlessList but using a sectioned adapter...The sections will be pages of items that will be downloaded from the Internet.

I've tried to implement the basic gist of it using both a MergeAdapter or the deprecated SectionedAdapter (by Mark Murphy). But I'm facing issues with both of them.

1) Using the SectionedAdapter, It loads the first page fine, but on the second page I run into this exception.

Uncaught handler: thread main exiting due to uncaught exception
java.lang.ArrayIndexOutOfBoundsException
at android.widget.AbsListView$RecycleBin.addScrapView(AbsListView.java:3572)
at android.widget.AbsListView.trackMotionScroll(AbsListView.java:2487)
at android.widget.AbsListView$FlingRunnable.run(AbsListView.java:2353)
at android.os.Handler.handleCallback(Handler.java:587)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4363)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
at dalvik.system.NativeStart.main(Native Method)

which after hours of research, i've realized it's something to do with a wrong getViewTypeCount() value. But I have no idea how to implement it correctly.

2) Then I tried using the MergeAdapter, but that simply made it worse, even the Sections appear at random intervals.

I've written a small example program just to show my implementation.

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

import android.app.ListActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ArrayAdapter;
import android.widget.TextView;

public class LazySectionListActivity extends ListActivity implements
        OnScrollListener {

    private int mPrevItemCount;
    private int nextPrimes = 0;
    private final int MAX = 100;
    private final SectionAdapter adapter = new SectionAdapter();

    // private final MergeAdapter adapter = new MergeAdapter();

    private class SectionAdapter extends SectionedAdapter {

        @Override
        protected View getHeaderView(String caption, int index,
                                     View convertView, ViewGroup parent) {
            TextView tv = new TextView(LazySectionListActivity.this);
            tv.setText(caption);
            return tv;
        }

    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
        setProgressBarIndeterminate(true);
        setContentView(R.layout.main);
        getListView().setOnScrollListener(this);
        setListAdapter(adapter);
        new NextThousandPrimes(nextPrimes).execute();
        nextPrimes += MAX;
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem,
                         int visibleItemCount, int totalItemCount) {

        if(firstVisibleItem + visibleItemCount >= totalItemCount
           && mPrevItemCount != totalItemCount) {
            new NextThousandPrimes(nextPrimes).execute();
            nextPrimes += MAX;
            mPrevItemCount = totalItemCount;
        }

    }

    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {

    }

    private class NextThousandPrimes extends AsyncTask<Void, Void, Void> {
        private final int start;
        private final List<String> primes = new ArrayList<String>();

        public NextThousandPrimes(int nextPrimes) {
            this.start = nextPrimes;
        }

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            setProgressBarIndeterminateVisibility(true);
        }

        @Override
        protected Void doInBackground(Void... params) {
            for(int s = start; s < start + MAX; s++)
                if(BigInteger.valueOf(s).isProbablePrime(1)) {
                    primes.add(String.valueOf(s));
                }
            return null;
        }

        @Override
        protected void onPostExecute(Void result) {
            super.onPostExecute(result);
            setProgressBarIndeterminateVisibility(false);
            //TextView view = new TextView(LazySectionListActivity.this);//for merge adapter, use the commented code
            //view.setText(String.format("%d-%d", start, start + MAX));
            // adapter.addView(view);
            // adapter.addAdapter(new
            // ArrayAdapter<String>(LazySectionListActivity.this,
            // android.R.layout.simple_list_item_1,
            // primes));
            adapter.addSection(String.format("%d-%d", start, start + MAX),
                               new ArrayAdapter<String>(LazySectionListActivity.this,
                                                        android.R.layout.simple_list_item_1,
                                                        primes));
            adapter.notifyDataSetChanged();

        }
    }
}

So what am i missing? Any help is appreciated. Thanks in advance.

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
st0le
  • 33,375
  • 8
  • 89
  • 89

1 Answers1

5

As stated in the MergeAdapter documentation:

You must pour the contents into the MergeAdapter before calling setListAdapter() to associate the MergeAdapter with a ListView. This limitation is required because Android only calls getViewTypeCount() once, and adding more views or adapters adds more view types.

Hence, MergeAdapter does not work in an endless setting, because we do not know how many possible view types there are.

You are welcome to try hacking a copy of MergeAdapter, where you wire getViewTypeCount() to be some value you are sure will be bigger than your needs, and see if that works. As the saying goes, your mileage may vary... :-)

CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
  • Hi Mark, AFAIK the `getViewTypeCount()` refers to the different kinds of views present in the entire ListView. But In my case, I know it only 2, (the section and the actual data list item). If the MergeAdapter returns `Viewtypecount = 2` shouldn't that suffice? – st0le Mar 15 '12 at 12:57
  • @st0le: Only if you override `getViewTypeCount()` and `getItemViewType()` and manage those values manually, in which case you do not really need `MergeAdapter` and could do it all yourself. – CommonsWare Mar 15 '12 at 13:09
  • Aah, So i'll try hacking your sectioned adapter. Thanks again. – st0le Mar 16 '12 at 03:42