Search code examples
androidandroid-cursoradapter

CursorAdapter: getCount() == 0, mCursor.getCount() == 6


I have an adapter that wraps a CursorAdapter, the reason being that I need to display items in rows (as in a GridView) but also display detail about each item if clicked in a panel below (requiring a ListView). So instead of a CursorAdapter directly populating the ListView, I have an inner adapter:

public class ChallengeAdapter extends BaseAdapter {

    class ChallengeDataAdapter extends CursorAdapter {

        private BaseAdapter mChallengeAdapter;

        public ChallengeDataAdapter(Context context, Cursor cursor, BaseAdapter a) {
            super(context, cursor, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
            mChallengeAdapter = a;
        }

        @Override
        public void bindView(View arg0, Context arg1, Cursor arg2) {
            // TODO Auto-generated method stub

        }

        @Override
        public View newView(Context arg0, Cursor arg1, ViewGroup arg2) {
            // TODO Auto-generated method stub
            return null;
        }

        @Override
        protected void onContentChanged() {
            super.onContentChanged();
            mChallengeAdapter.notifyDataSetChanged();
        }

    }

    private ChallengeDataAdapter mDataAdapter;

    public ChallengeAdapter(Context context, Cursor cursor) {
        mDataAdapter = new ChallengeDataAdapter(context, cursor, this);
    }

    @Override
    public int getCount() {
        return (mDataAdapter.getCount() + ChallengeMultiTile.ROW_SIZE - 1) / ChallengeMultiTile.ROW_SIZE; //integer divide rounds down
    }

    @Override
    public Object getItem(int position) {
        Challenge[] output = new Challenge[ChallengeMultiTile.ROW_SIZE];
        int min = position * ChallengeMultiTile.ROW_SIZE;
        int max = Math.min(position * ChallengeMultiTile.ROW_SIZE + ChallengeMultiTile.ROW_SIZE, mDataAdapter.getCount());
        for(int i=min; i<max; ++i) {
            output[i-min] = Challenge.get((Cursor) mDataAdapter.getItem(i));
        }
        return output;
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView==null) {
            convertView = new ChallengeMultiTile(parent.getContext());
        }
        ((ChallengeMultiTile) convertView).populate((Challenge[]) getItem(position));
        convertView.setBackgroundColor(0xfffffff);
        return convertView;
    }

    public void swapCursor(Cursor cursor) {
        mDataAdapter.swapCursor(cursor);
        notifyDataSetChanged();
    }

}

This works briefly, but then the ChallengeDataAdapter starts to have a getCount() of zero. However, I put a breakpoint there and ChallengeDataAdapter#mCursor.getCount() is 6. I trapped this in the onContentChanged() method but I don't know for sure that's where it's happening.

Why would a CursorAdapter give a count of zero when its inner cursor gives a count of six?


Solution

  • When you look at the CursorAdapter source code you'll see the following for getCount() and onContentChanged():

    public int getCount() {
        if (mDataValid && mCursor != null) {
            return mCursor.getCount();
        } else {
            return 0;
        }
    }
    
    protected void onContentChanged() {
        if (mAutoRequery && mCursor != null && !mCursor.isClosed()) {
            if (false) Log.v("Cursor", "Auto requerying " + mCursor + " due to update");
            mDataValid = mCursor.requery();
        }
    }
    

    So while mCursor.getCount() might return 6, getCount() might return 0 if the requery failed. Why the requery fails is another question and not one I could answer from the code you posted.

    BTW you don't need to keep a reference to the ChallengeAdapter in your ChallengeDataAdapter class. ChallengeDataAdapter is an inner class which is associated with an instance of its enclosing class and has direct access to that object's methods and fields. Instead of calling mChallengeAdapter.notifyDataSetChanged() you could just do a ChallengeAdapter.this.notifyDataSetChanged();