Understanding GridView in Android with Custom Adapters by Building an Image Gallery

GridView is a ViewGroup just like ListView that allows us to display items in a two-dimensional scrolling grid. Just like a ListView the data into a GridView is populated by an Adapter (ListAdapter) that fetches data from various sources like an array or a database. It’s mostly used to build image, video, audio galleries.

Let’s see how we can fetch all the image media on the system and display their thumbnails in the GridView to build an Image Gallery.

What's the one thing every developer wants? More screens! Enhance your coding experience with an external monitor to increase screen real estate.

Creating the GridView Layout

We’ll put this piece of code inside layout/activity_photo_gallery.xml to create a GridView:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <GridView
        android:id="@+id/gridView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:columnWidth="100dp"
        android:numColumns="auto_fit"
        android:stretchMode="columnWidth"
        android:gravity="center" />

</LinearLayout>

The attributes are all easy to understand and well documented.

Creating Item Layout for GridView Items

We created our master layout that defines the grid. Next, we’ll define a layout for each items in the grid in layout/conversation_item.xml. Our item layout will have only 1 View which is the ImageView:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/imageView" />
</LinearLayout>

Custom GridView Adapter

We’ll need an adapter to render the grid items into the GridView. For that we’ll create a custom adapter called ImageView that’ll lie inside our Activity class as its inner class extending BaseAdapter.

class ImageAdapter extends BaseAdapter {

    private Context mContext;

    public ImageAdapter(Context context) {
        mContext = context;
    }

    @Override
    public int getCount() {
        return mCursor.getCount();
    }

    @Override
    public Object getItem(int position) {
        return null;
    }

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

    // Convert DP to PX
    // Source: http://stackoverflow.com/a/8490361
    public int dpToPx(int dps) {
        final float scale = getResources().getDisplayMetrics().density;
        int pixels = (int) (dps * scale + 0.5f);

        return pixels;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        int imageID = 0;

        // Want the width/height of the items
        // to be 120dp
        int wPixel = dpToPx(120);
        int hPixel = dpToPx(120);

        // Move cursor to current position
        mCursor.moveToPosition(position);
        // Get the current value for the requested column
        imageID = mCursor.getInt(columnIndex);

        if (convertView == null) {
            // If convertView is null then inflate the appropriate layout file
            convertView = LayoutInflater.from(mContext).inflate(R.layout.conversation_item, null);
        }
        else {

        }

        imageView = (ImageView) convertView.findViewById(R.id.imageView);

        // Set height and width constraints for the image view
        imageView.setLayoutParams(new LinearLayout.LayoutParams(wPixel, hPixel));

        // Set the content of the image based on the provided URI
        imageView.setImageURI(
                Uri.withAppendedPath(MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI, "" + imageID)
        );

        // Image should be cropped towards the center
        imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);

        // Set Padding for images
        imageView.setPadding(8, 8, 8, 8);

        // Crop the image to fit within its padding
        imageView.setCropToPadding(true);

        return convertView;
    }
}

Firstly we override a couple of required methods from BaseAdapter:

  • Constructor – That’s self-explanatory.
  • getCount() – Should return the number of items in the data set represented by the Adapter implementation.
  • getItem() – Should return the data item at the specified position (passed to it as an argument) of the data set.
  • getItemId() – Should return the row ID (unique) associated with the specified position (passed as an argument) in the data set. To get an in-depth understanding of Item IDs and how to use them, here’s a nice SO answer that you must read. Basically you can attach unique IDs to every item in a GridView or ListView which might have different types of items hence using the position might not help with fetching the right item from the data set. A good example is a ListView with rows and separators (used as group headers) where the items are checkable/selectable. Now the position for the first element from the data set should be 0 but in the list will be 1st as the header is the 0th element. It gets complicated when you integrate search to the list which filters data in realtime and then hit on the checkbox to select items (lets say contacts). If every row is assigned a unique ID then using getCheckedItemIds() we can get a list of rows checked with the given item IDs and use the list of item IDs to fetch appropriate items form our data set for further requirements.

getItem() and getItemId() are both ignored by returning null and 0 as we don’t really need them in this context.

getView() method is where the real work is done of creating the Views for each item (image) of the GridView. Whenever convertView is null, which means a recycled View object is not passed, we inflate the XML layout file which creates different View objects from the contents of the file.

Finally we use various functions to customize our ImageView according to our needs:

Using the Custom GridView Adapter in the Activity

Using the adapter to populate the GridView has to be done in the Activity class. We’ll write all the relevant code in our onCreate() method:


protected Cursor mCursor;
protected int columnIndex;
protected GridView mGridView;
protected ImageAdapter mAdapter;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_conversations);

    // Get all the images on phone

    String[] projection = {
            MediaStore.Images.Thumbnails._ID,
            MediaStore.Images.Thumbnails.IMAGE_ID
    };

    mCursor = getContentResolver().query(
            MediaStore.Images.Thumbnails.EXTERNAL_CONTENT_URI,
            projection,
            null,
            null,
            MediaStore.Images.Thumbnails.IMAGE_ID + " DESC"
    );

    columnIndex = mCursor.getColumnIndexOrThrow(projection[0]);

    // Get the GridView layout
    mGridView = (GridView) findViewById(R.id.gridView);
    mAdapter = new ImageAdapter(this);
    mGridView.setAdapter(mAdapter);
}

Very basic code, we used the MediaStore Content Provider to fetch all the thumbnails of all the images on our system. Then we get the GridView View object using findViewById() and instantiate our ImageAdapter (custom adapter) which is bound to mGridView using setAdapter().

Bonus

Attaching Click Events to the GridView Items

If required, we can attach click events to the GridView items like this:

mGridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        Toast.makeText(ConversationsActivity.this, "Selected Position: " + position, Toast.LENGTH_SHORT).show();
    }
});

This piece of code should go at the end of onCreate() method and will show a Toast message whenever an item is tapped (or clicked). The message will prompt the position of the clicked item. This can be useful to let’s say show the real image taking up the entire screen space on tap. For a long press listener there’s setOnItemLongClickListener().

Customizing the Background Color of Grid Item on Press

When pressing an item in the grid, by default it highlights in some color. We can change that if we want to. For that first we’ll have to define the colors in a drawable resource:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="@android:integer/config_mediumAnimTime">

    <item android:drawable="@color/blue" android:state_pressed="true"/>
    <item android:drawable="@color/blue" android:state_selected="true"/>
    <item android:drawable="@color/transparent"/>

</selector>

Let’s say this code is placed in res/drawable/list_selector.xml, we’ll need to reference it from the grid view layout file by adding this attribute to the GridView element – android:listSelector="@drawable/list_selector". That’s it! On pressed and selected states the background color will be blue, else it’ll be transparent.

Using an inbuilt Adapter like ArrayAdapter

Built-in adapters can be used by writing your own custom adapters that extend them. But if you want to refrain from writing your custom adapters and simply use let’s say ArrayAdapter with a GridView for some purpose like testing then it’s mighty simple:

String[] months = { "Jan", "Feb", "Mar", "Apr", "May", "June", "July", "Aug", "Sept", "Oct", "Nov", "Dec" };
// Get the GridView layout
mGridView = (GridView) findViewById(R.id.gridView);
mAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, months);
mGridView.setAdapter(mAdapter);

Conclusion

Hopefully with this image gallery example, you’ll understand how easy it is to build grids in an Android app by using the GridView view group. Use an in-built adapter or write a custom adapter for further control over the layout and views and attach it to the gridview.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download

Author: Rishabh

Rishabh is a full stack web and mobile developer from India. Follow me on Twitter.

Leave a Reply

Your email address will not be published. Required fields are marked *