CursorLoader
is a specialized subclass of AsyncTaskLoader
that uses its lifecycle methods to correctly manage the resources associated with a database Cursor
.
A database Cursor
is a little like an Iterator
, in that it allows you to scroll through a dataset without having to worry where exactly the dataset is coming from or what data structure it is a part of.
We're going to use CursorLoader
to query the MediaStore for a list of all images on the device. Because CursorLoader
is already implemented to correctly handle all of the details of working with a Cursor
, we don't need to subclass it. We can simply instantiate it, passing in the information it needs in order to open the Cursor
it should manage for us. We can do this in the onCreateLoader
callback:
@Override public CursorLoader onCreateLoader(int id, Bundle bundle) { return new CursorLoader(this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{ MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME }, "", null, null); }
Just as with the previous example, we'll implement the callbacks in our Activity
subclass. We're going to use GridView
to display our loaded results, so we'll implement an Adapter
interface to supply views
for its cells, and we'll connect the Adapter
to the Cursor
created by our Loader
:
public class MediaStoreActivity extends Activity implements LoaderManager.LoaderCallbacks<Cursor> { public static final int MS_LOADER = "ms_crsr".hashCode(); private MediaCursorAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.images); adapter = new MediaCursorAdapter( getApplicationContext()); GridView grid = (GridView)findViewById(R.id.grid); grid.setAdapter(adapter); getLoaderManager().initLoader(MS_LOADER, null, this); } @Override public CursorLoader onCreateLoader(int id, Bundle bundle) { return new CursorLoader(this, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[]{ MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME }, "", null, null); } @Override public void onLoadFinished( Loader<Cursor> loader, Cursor media) { adapter.changeCursor(media); } @Override public void onLoaderReset(Loader<Cursor> loader) { adapter.changeCursor(null); } }
Have a look at the parts in bold in the previous code. We create a MediaCursorAdapter
, and pass it to the GridView
, we then initialize our CursorLoader
. When loading is completed, we pass the loaded Cursor
to the Adapter
, and we're done.
The remaining piece to implement is MediaCursorAdapter
, which is going to start out as a very simple class. The job of our CursorAdapter
is simply to map rows of data from the Cursor
to each View
in the individual GridView
cells.
The Android SDK provides the very handy SimpleCursorAdapter
class, which does just what we need. So for now we'll just subclass it and instruct it, via constructor parameters, which layout to inflate for each cell and the Cursor
fields to map to each View
within that layout.
public class MediaCursorAdapter extends SimpleCursorAdapter { private static String[] FIELDS = new String[]{ MediaStore.Images.Media._ID, MediaStore.Images.Media.DISPLAY_NAME }; private static int[] VIEWS = new int[]{ R.id.media_id, R.id.display_name }; public MediaCursorAdapter(Context context) { super(context, R.layout.example2_cell, null, FIELDS, VIEWS, 0); } }
The layout files and source code are available on the accompanying website. When you run this Activity
, you'll see a two-column grid where each cell contains the ID and display name of an image from the MediaStore. If, like me, you have a lot of images on your device, you can fling the grid and watch the file names spin by.
Scroll to somewhere in the middle of the list and rotate your device, and you'll notice that the Activity
restarts and redisplays the grid immediately, without losing its place—this is because the CursorLoader
survived the restart, and still holds the Cursor
object with the same rows loaded.
This is technically all very interesting, but it isn't much to look at. In the next section we'll combine our two Loaders to implement a scrollable grid displaying the images themselves.
3.139.86.131