Skip to content

Instantly share code, notes, and snippets.

@peterkuterna
Created July 19, 2012 14:20
Show Gist options
  • Star 35 You must be signed in to star a gist
  • Fork 10 You must be signed in to fork a gist
  • Save peterkuterna/3144266 to your computer and use it in GitHub Desktop.
Save peterkuterna/3144266 to your computer and use it in GitHub Desktop.
A FragmentPagerAdapter that exposes data from a Cursor
package com.example;
import android.content.Context;
import android.database.Cursor;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.util.SparseIntArray;
import android.view.ViewGroup;
import java.util.HashMap;
public abstract class CursorFragmentPagerAdapter extends FragmentPagerAdapter {
protected boolean mDataValid;
protected Cursor mCursor;
protected Context mContext;
protected SparseIntArray mItemPositions;
protected HashMap<Object, Integer> mObjectMap;
protected int mRowIDColumn;
public CursorFragmentPagerAdapter(Context context, FragmentManager fm, Cursor cursor) {
super(fm);
init(context, cursor);
}
void init(Context context, Cursor c) {
mObjectMap = new HashMap<Object, Integer>();
boolean cursorPresent = c != null;
mCursor = c;
mDataValid = cursorPresent;
mContext = context;
mRowIDColumn = cursorPresent ? c.getColumnIndexOrThrow("_id") : -1;
}
public Cursor getCursor() {
return mCursor;
}
@Override
public int getItemPosition(Object object) {
Integer rowId = mObjectMap.get(object);
if (rowId != null && mItemPositions != null) {
return mItemPositions.get(rowId, POSITION_NONE);
}
return POSITION_NONE;
}
public void setItemPositions() {
mItemPositions = null;
if (mDataValid) {
int count = mCursor.getCount();
mItemPositions = new SparseIntArray(count);
mCursor.moveToPosition(-1);
while (mCursor.moveToNext()) {
int rowId = mCursor.getInt(mRowIDColumn);
int cursorPos = mCursor.getPosition();
mItemPositions.append(rowId, cursorPos);
}
}
}
@Override
public Fragment getItem(int position) {
if (mDataValid) {
mCursor.moveToPosition(position);
return getItem(mContext, mCursor);
} else {
return null;
}
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
mObjectMap.remove(object);
super.destroyItem(container, position, object);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (!mDataValid) {
throw new IllegalStateException("this should only be called when the cursor is valid");
}
if (!mCursor.moveToPosition(position)) {
throw new IllegalStateException("couldn't move cursor to position " + position);
}
int rowId = mCursor.getInt(mRowIDColumn);
Object obj = super.instantiateItem(container, position);
mObjectMap.put(obj, Integer.valueOf(rowId));
return obj;
}
public abstract Fragment getItem(Context context, Cursor cursor);
@Override
public int getCount() {
if (mDataValid) {
return mCursor.getCount();
} else {
return 0;
}
}
public void changeCursor(Cursor cursor) {
Cursor old = swapCursor(cursor);
if (old != null) {
old.close();
}
}
public Cursor swapCursor(Cursor newCursor) {
if (newCursor == mCursor) {
return null;
}
Cursor oldCursor = mCursor;
mCursor = newCursor;
if (newCursor != null) {
mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
mDataValid = true;
} else {
mRowIDColumn = -1;
mDataValid = false;
}
setItemPositions();
notifyDataSetChanged();
return oldCursor;
}
@Override
public long getItemId(int position) {
if (!mDataValid || !mCursor.moveToPosition(position)) {
return super.getItemId(position);
}
int rowId = mCursor.getInt(mRowIDColumn);
return rowId;
}
}
@camdenfullmer
Copy link

There is a IllegalStateException that is thrown when trying to destroy the Activity because swapCursor is generally called with a null cursor to remove any of the previous cursor references.

@erikeelde
Copy link

We fixed it with:

   public Cursor swapCursor(Cursor newCursor) {
        if (newCursor == mCursor) {
            return null;
        }
        Cursor oldCursor = mCursor;
        mCursor = newCursor;
        if (newCursor != null) {
            mRowIDColumn = newCursor.getColumnIndexOrThrow(BaseColumns._ID);
            mDataValid = true;
            notifyDataSetChanged();
        } else {
            mRowIDColumn = -1;
            mDataValid = false;
        }

        setItemPositions();

        return oldCursor;
    }

Moving the notifyDataSetChanged into the if-statement better mimics the CursorAdapter-behaviour also.

@aegis1980
Copy link

Hey. Thanks for this @peterkuterna
I had some issues with data not updating on calling swapCursor - changed as follows:

    public Cursor swapCursor(Cursor newCursor) {
        if (newCursor == mCursor) {
            return null;
        }
        Cursor oldCursor = mCursor;
        mCursor = newCursor;
        if (newCursor != null) {
            mRowIDColumn = newCursor.getColumnIndexOrThrow("_id");
            mDataValid = true;

        } else {
            mRowIDColumn = -1;
            mDataValid = false;
        }

        setItemPositions();
        if (mDataValid){
             notifyDataSetChanged();
        }


        return oldCursor;
    }

...if it helps anyone. Seems to work for me,

I forked this gist , with above change and subclassing of FragmentStatePagerAdapter rather than FragmentPagerAdapter - just removed getItemId(int pos) Seem to work well. https://gist.github.com/aegis1980/95b757047749ae19c484

@Igorpi25
Copy link

Igorpi25 commented Mar 1, 2015

Thank you. It works fine.
P.S: Android is so feeble so we forced to reinvent the wheel for every 5 minites. And What is reason to believe it so great? I'm developing android apps for 4th year and I use third hand's codes everywhere and every case. Is that so?

@IEnoobong
Copy link

Please how do i use this in my code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment