Building responsive apps with CursorLoader

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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.141.42.5